簡體   English   中英

Cant Pickle記住了類實例

[英]Cant Pickle memoized class instance

這是我正在使用的代碼

import funcy

@funcy.memoize
class mystery(object):

    def __init__(self, num):
        self.num = num

feat = mystery(1)

with open('num.pickle', 'wb') as f:
    pickle.dump(feat,f)

這給了我以下錯誤:

PicklingError: Can't pickle <class '__main__.mystery'>: it's not the 
same object as __main__.mystery

我希望1)理解為什么會這樣,2)找到一個允許我挑選對象的解決方案(不刪除memoization)。 理想情況下,解決方案不會改變對pickle的調用。

用funcy運行python 3.6 == 1.10

問題是你已經為一個類應用了一個為函數設計的裝飾器。 結果不是一個類,而是一個包裝對類的調用的函數。 這導致了許多問題(例如,正如Aran-Fey在評論中指出的那樣,你不可能是isinstance(feat, mystery) ,因為mystery )。

但是你關心的特殊問題是你不能挑選無法訪問的類的實例。

事實上,這基本上是錯誤消息告訴你的:

PicklingError: Can't pickle <class '__main__.mystery'>: it's not the 
same object as __main__.mystery

你的feat認為它的類型是__main__.mystery ,但這根本不是一個類型,它是包裝該類型的裝飾器返回的函數。


解決這個問題的簡單方法是找到一個類裝飾器,它可以滿足您的需求。 它可能被稱為flyweight而不是memoize ,但我確信存在大量的例子。


但是你可以通過記住構造函數來構建一個flyweight類,而不是記住這個類:

class mystery:
    @funcy.memoize
    def __new__(cls, num):
        return super().__new__(cls)
    def __init__(self, num):
        self.num = num

...雖然您可能希望在這種情況下將初始化移動到構造函數中。 否則,調用mystery(1)然后mystery(1)將作為前返回相同的對象,但也有重新初始化self.num = 1 ,這充其量是浪費的,在最壞的情況不正確。 所以:

class mystery:
    @funcy.memoize
    def __new__(cls, num):
        self = super().__new__(cls)
        self.num = num
        return self

現在:

>>> feat = mystery(1)
>>> feat
<__main__.mystery at 0x10eeb1278>
>>> mystery(2)
<__main__.mystery at 0x10eeb2c18>
>>> mystery(1)
<__main__.mystery at 0x10eeb1278>

而且,因為feat的類型現在是一個可以在模塊 - 全局名稱mystery下訪問的類,所以pickle將完全沒有問題:

>>> pickle.dumps(feat)
b'\x80\x03c__main__\nmystery\nq\x00)\x81q\x01}q\x02X\x03\x00\x00\x00numq\x03K\x01sb.'

還是要考慮這個類應該如何與酸洗玩。 特別是,你想要unpickling通過緩存嗎? 默認情況下,它不會:

>>> pickle.loads(pickle.dumps(feat)) is feat
False

發生的事情是它使用默認的__reduce_ex__進行酸洗,默認情況下相當於(僅略微過度簡化):

result = object.__new__(__main__.mystery)
result.__dict__.update({'num': 1})

如果您希望它通過緩存,最簡單的解決方案是:

class mystery:
    @funcy.memoize
    def __new__(cls, num):
        self = super().__new__(cls)
        self.num = num
        return self
    def __reduce__(self):
        return (type(self), (self.num,))

如果你計划這么做,你可能會想到編寫自己的類裝飾器:

def memoclass(cls):
    @funcy.memoize
    def __new__(cls, *args, **kwargs):
        return super(cls, cls).__new__(cls)
    cls.__new__ = __new__
    return cls

但是這個:

  • ......有點難看,
  • ...僅適用於不需要將構造函數參數傳遞給基類的類,
  • ...僅適用於沒有__init__ (或者至少具有冪等且快速的__init__ ,無法重復調用),
  • ...沒有提供一種簡單的方法來勾選酸洗,並且
  • ......不記錄或測試任何這些限制。

所以,我認為你最好是明確的,只是__new__方法,或者寫一些(或找到)更多更好的東西,這樣做的內省需要以這種方式完全記住一個類。 (或者,也可以寫一個僅適用於某些受限制的類集合的類 - 例如@memodataclass就像@dataclass但是使用memoized構造函數將比完全通用的@memoclass容易得多。)

另一種方法是

class _mystery(object):

    def __init__(self, num):
        self.num = num

@funcy.memoize
def mystery(num):
    return _mystery(num)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM