繁体   English   中英

Cant Pickle记住了类实例

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM