gpt4 book ai didi

python - Pickle 和装饰类(PicklingError : not the same object)

转载 作者:行者123 更新时间:2023-11-28 20:57:57 25 4
gpt4 key购买 nike

下面的最小示例使用了一个虚拟装饰器,它只是在构造装饰类的对象时打印一些消息。

import pickle


def decorate(message):
def call_decorator(func):
def wrapper(*args, **kwargs):
print(message)
return func(*args, **kwargs)

return wrapper

return call_decorator


@decorate('hi')
class Foo:
pass


foo = Foo()
dump = pickle.dumps(foo) # Fails already here.
foo = pickle.loads(dump)

然而,使用它会使 pickle 引发以下异常:

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

我能做些什么来解决这个问题吗?

最佳答案

Pickle 要求实例的__class__ 属性可以通过导入加载。

Pickling 实例仅存储实例数据,该类的 __qualname____module__ 属性用于稍后通过再次导入该类来重新创建实例,并且为该类创建一个新实例。

Pickle 验证该类实际上可以首先导入。 __module____qualname__ 对用于查找正确的模块,然后访问该模块上由 __qualname__ 命名的对象,如果 __class__ 对象和在模块上找到的对象不匹配,会引发您看到的错误。

这里,foo.__class__ 指向一个 __qualname__ 设置为 'Foo'__module__ 的类对象设置为 '__main__',但是 sys.modules['__main__'].Foo 不指向一个类,而是指向一个函数,包装器 装饰器返回的嵌套函数。

有两种可能的解决方案:

  • 不要返回函数,返回原始类,并可能检测类对象来完成包装器所做的工作。如果您对类构造函数的参数进行操作,请在装饰类上添加或包装 __new____init__ 方法。

    考虑到 unpickling 通常在类上调用 __new__ 来创建一个新的空实例,然后再恢复实例状态(除非 pickling 已经是 customised )。

  • 将类存储在新位置。更改类的 __qualname__ 和可能的 __module__ 属性,以指向 pickle 可以找到原始类的位置。在 unpickling 上,将再次创建正确类型的实例,就像原始的 Foo() 调用一样。

另一种选择是为生成的类自定义 pickle 。你可以给类(class)new __reduce_ex__new __reduce__相反,指向包装函数或自定义 reduce 函数的方法。这可能会变得复杂,因为该类可能已经自定义了 pickling,并且 object.__reduce_ex__ 提供了默认值,并且返回值可能因 pickle 版本而异。

如果你不想改变类,你也可以使用 copyreg.pickle() function为该类注册自定义 __reduce__ 处理程序。

无论哪种方式,reducer 的返回值仍应避免引用该类,而应引用新的构造函数,使用它可以导入的名称。如果您直接将装饰器与 new_name = decorator()(classobj) 一起使用,这可能会出现问题。 Pickle 本身也不会处理这种情况(因为 classobj.__name__ 不会匹配 newname)

关于python - Pickle 和装饰类(PicklingError : not the same object),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52185507/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com