这是我正在使用的代码
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)找到一种解决方案,使我可以对对象进行腌制(不删除备忘录)。理想情况下,该解决方案不会更改对pickle的调用。
使用funcy == 1.10运行python 3.6
最佳答案
问题是您已将为函数设计的装饰器应用于类。结果不是一个类,而是一个包装对该类的调用的函数。这会导致许多问题(例如,正如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.'
您仍然想考虑该类如何与酸洗一起使用。特别是,您是否要取消筛选才能通过缓存?默认情况下,它不会:
>>> 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__
方法,或者编写(或查找)一些奇特的东西以进行内省(introspection),以使这种方式完全通用性地记住一个类。 (或者,替代地,编写只适用于某些受限类集的代码,例如,类似于@memodataclass
但具有内存构造函数的@dataclass
比完全通用的@memoclass
容易得多。)关于python - Cant Pickle内存类实例,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51831733/