Cant Pickle memoized class 个实例
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) 找到一个允许我腌制对象的解决方案(不删除记忆)。理想情况下,解决方案不会更改对 pickle 的调用。
运行 python 3.6 功能==1.10
问题是您将专为函数设计的装饰器应用到 class。结果不是 class,而是一个包含对 class 调用的函数。这会导致许多问题(例如,正如 Aran-Fey 在评论中指出的那样,您不能 isinstance(feat, mystery)
,因为 mystery
)。
但是您关心的特定问题是您不能 pickle 无法访问的 classes 实例。
事实上,这基本上就是错误消息告诉您的内容:
PicklingError: Can't pickle <class '__main__.mystery'>: it's not the
same object as __main__.mystery
您的 feat
认为它的类型是 __main__.mystery
,但那根本不是一个类型,它是由包装该类型的装饰器 return 编辑的函数。
解决此问题的简单方法是找到一个 class 装饰器,它可以满足您的需求。它可能被称为 flyweight
而不是 memoize
,但我确信存在大量示例。
但是你可以通过只记忆构造函数来构建享元 class,而不是记忆 class:
class mystery:
@funcy.memoize
def __new__(cls, num):
return super().__new__(cls)
def __init__(self, num):
self.num = num
… 尽管在这种情况下您可能希望将初始化移到构造函数中。否则,调用 mystery(1)
然后 mystery(1)
将 return 与之前相同的对象,但还会用 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
的类型现在是 class,可以在模块全局名称 mystery
下访问,所以 pickle
在全部:
>>> pickle.dumps(feat)
b'\x80\x03c__main__\nmystery\nq\x00)\x81q\x01}q\x02X\x03\x00\x00\x00numq\x03K\x01sb.'
你做还想想想这个class应该怎么玩pickling。特别是,您是否希望 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,))
如果您打算经常这样做,您可能会考虑编写自己的 class 装饰器:
def memoclass(cls):
@funcy.memoize
def __new__(cls, *args, **kwargs):
return super(cls, cls).__new__(cls)
cls.__new__ = __new__
return cls
但是这个:
- ……有点丑,
- … 仅适用于不需要将构造函数参数传递给基础 class、
的 classes
- … 仅适用于没有
__init__
的 classes(或者,至少,具有幂等且快速的 __init__
且重复调用无害),
- …不提供简单的挂钩酸洗方法,并且
- …不记录或测试任何这些限制。
所以,我认为你最好明确一点,只记住 __new__
方法,或者编写(或发现)一些更高级的东西来进行内省,使记忆成为 class 这种方式完全通用。 (或者,或者,也许写一个只适用于某些受限制的 classes 集的一个 - 例如,一个 @memodataclass
就像 @dataclass
但使用记忆构造函数会比一个完全通用的 @memoclass
.)
另一种方法是
class _mystery(object):
def __init__(self, num):
self.num = num
@funcy.memoize
def mystery(num):
return _mystery(num)
这是我使用的代码
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 的调用。
运行 python 3.6 功能==1.10
问题是您将专为函数设计的装饰器应用到 class。结果不是 class,而是一个包含对 class 调用的函数。这会导致许多问题(例如,正如 Aran-Fey 在评论中指出的那样,您不能 isinstance(feat, mystery)
,因为 mystery
)。
但是您关心的特定问题是您不能 pickle 无法访问的 classes 实例。
事实上,这基本上就是错误消息告诉您的内容:
PicklingError: Can't pickle <class '__main__.mystery'>: it's not the
same object as __main__.mystery
您的 feat
认为它的类型是 __main__.mystery
,但那根本不是一个类型,它是由包装该类型的装饰器 return 编辑的函数。
解决此问题的简单方法是找到一个 class 装饰器,它可以满足您的需求。它可能被称为 flyweight
而不是 memoize
,但我确信存在大量示例。
但是你可以通过只记忆构造函数来构建享元 class,而不是记忆 class:
class mystery:
@funcy.memoize
def __new__(cls, num):
return super().__new__(cls)
def __init__(self, num):
self.num = num
… 尽管在这种情况下您可能希望将初始化移到构造函数中。否则,调用 mystery(1)
然后 mystery(1)
将 return 与之前相同的对象,但还会用 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
的类型现在是 class,可以在模块全局名称 mystery
下访问,所以 pickle
在全部:
>>> pickle.dumps(feat)
b'\x80\x03c__main__\nmystery\nq\x00)\x81q\x01}q\x02X\x03\x00\x00\x00numq\x03K\x01sb.'
你做还想想想这个class应该怎么玩pickling。特别是,您是否希望 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,))
如果您打算经常这样做,您可能会考虑编写自己的 class 装饰器:
def memoclass(cls):
@funcy.memoize
def __new__(cls, *args, **kwargs):
return super(cls, cls).__new__(cls)
cls.__new__ = __new__
return cls
但是这个:
- ……有点丑,
- … 仅适用于不需要将构造函数参数传递给基础 class、 的 classes
- … 仅适用于没有
__init__
的 classes(或者,至少,具有幂等且快速的__init__
且重复调用无害), - …不提供简单的挂钩酸洗方法,并且
- …不记录或测试任何这些限制。
所以,我认为你最好明确一点,只记住 __new__
方法,或者编写(或发现)一些更高级的东西来进行内省,使记忆成为 class 这种方式完全通用。 (或者,或者,也许写一个只适用于某些受限制的 classes 集的一个 - 例如,一个 @memodataclass
就像 @dataclass
但使用记忆构造函数会比一个完全通用的 @memoclass
.)
另一种方法是
class _mystery(object):
def __init__(self, num):
self.num = num
@funcy.memoize
def mystery(num):
return _mystery(num)