python - 不能 Pickle memoize 类实例

标签 python python-3.x memoization 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)

这给了我以下错误:

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 )。

但是您特别关心的问题是您不能 pickle 不可访问类的实例。

事实上,这基本上就是错误消息告诉您的内容:

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

你的 feat认为它的类型是 __main__.mystery ,但这根本不是一个类型,它是包装该类型的装饰器返回的函数。


解决这个问题的简单方法是找到一个类装饰器来做你想做的事。它可能被称为 flyweight而不是 memoize ,但我确信存在大量示例。


但是您可以通过仅内存构造函数而不是内存类来构建享元类:

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.'

确实仍然想考虑这门课应该如何玩 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,))

如果您打算经常这样做,您可能会考虑编写自己的类装饰器:

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

但是这个:

  • ……有点丑,
  • … 仅适用于不需要将构造函数参数传递给基类的类,
  • … 仅适用于没有 __init__ 的类(或者,至少,具有幂等且快速的 __init__,重复调用无害),
  • … 没有提供简单的 hook pickling 方法,并且
  • …不记录或测试任何这些限制。

所以,我认为你最好不要直截了当,只记住 __new__方法,或者写(或发现)一些更奇特的东西来进行内省(introspection),使以这种方式内存一个类完全通用。 (或者,或者,也许写一个只适用于一些受限制的类集的一个 - 例如,一个 @memodataclass 就像 @dataclass 但使用内存构造函数会比完全通用的 @memoclass 容易得多。)

关于python - 不能 Pickle memoize 类实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51831733/

相关文章:

lisp - 我如何在 Lisp 中内存一个递归函数?

Python:pandas,解析数学运算

python - 使用python(通过请求或其他方式)检索下拉列表选择的html表

python - 错误绑定(bind)参数 0 : probably unsupported type

python - 将函数结果写入 stdin

javascript - JavaScript 记忆化只能用于缓存返回结果吗?

python - celery :如何限制队列中的任务数量并在满时停止喂食?

python - 解码十六进制(?)数据包

python - 为什么 Python 3 需要用 list() 包裹 dict.items?

algorithm - 动态规划 - 切杆自下而上算法 (CLRS) 解不正确?