hch*_*chw 5 python memoization python-3.x funcy
这是我正在使用的代码
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)
Run Code Online (Sandbox Code Playgroud)
这给了我以下错误:
PicklingError: Can't pickle <class '__main__.mystery'>: it's not the
same object as __main__.mystery
Run Code Online (Sandbox Code Playgroud)
我希望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
Run Code Online (Sandbox Code Playgroud)
你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
Run Code Online (Sandbox Code Playgroud)
...虽然您可能希望在这种情况下将初始化移动到构造函数中.否则,调用mystery(1)然后mystery(1)将返回与以前相同的对象,但也重新初始化它self.num = 1,这最多是浪费,最坏的是不正确.所以:
class mystery:
@funcy.memoize
def __new__(cls, num):
self = super().__new__(cls)
self.num = num
return self
Run Code Online (Sandbox Code Playgroud)
现在:
>>> feat = mystery(1)
>>> feat
<__main__.mystery at 0x10eeb1278>
>>> mystery(2)
<__main__.mystery at 0x10eeb2c18>
>>> mystery(1)
<__main__.mystery at 0x10eeb1278>
Run Code Online (Sandbox Code Playgroud)
而且,由于类型feat现在是一个类,是根据模块的全局名称访问mystery,pickle将与它一点问题都没有:
>>> pickle.dumps(feat)
b'\x80\x03c__main__\nmystery\nq\x00)\x81q\x01}q\x02X\x03\x00\x00\x00numq\x03K\x01sb.'
Run Code Online (Sandbox Code Playgroud)
你也还是要考虑这个类应该如何与酸洗玩.特别是,你想要unpickling通过缓存吗?默认情况下,它不会:
>>> pickle.loads(pickle.dumps(feat)) is feat
False
Run Code Online (Sandbox Code Playgroud)
发生的事情是它使用默认__reduce_ex__的酸洗,默认为相当于(仅略微过度简化):
result = object.__new__(__main__.mystery)
result.__dict__.update({'num': 1})
Run Code Online (Sandbox Code Playgroud)
如果您希望它通过缓存,最简单的解决方案是:
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,))
Run Code Online (Sandbox Code Playgroud)
如果你计划这么做,你可能会想到编写自己的类装饰器:
def memoclass(cls):
@funcy.memoize
def __new__(cls, *args, **kwargs):
return super(cls, cls).__new__(cls)
cls.__new__ = __new__
return cls
Run Code Online (Sandbox Code Playgroud)
但是这个:
__init__(或者至少具有幂等和快速__init__,无法重复调用)的类,所以,我认为你最好是明确的,只是memoizing的__new__方法,或写(或发现)的东西很多票友,做使记忆一个类这种方式完全通用需要反省.(或者,或者,也许写一个只适用于一些受限制的类集合的类 - 例如,一个@memodataclass类似@dataclass但是使用memoized构造函数将比完全通用更容易@memoclass.)