[英]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.