Python “我可以”;pickle local objects“;如果我使用派生类?
Python “我可以”;pickle local objects“;如果我使用派生类?,python,python-3.x,nested,pickle,Python,Python 3.x,Nested,Pickle,pickle引用可以被pickle的对象集是相当有限的。事实上,我有一个函数返回一个动态生成的类,我发现我不能pickle该类的实例: >>> import pickle >>> def f(): ... class A: pass ... return A ... >>> LocalA = f() >>> la = LocalA() >>> with open('testing.pick
pickle
引用可以被pickle的对象集是相当有限的。事实上,我有一个函数返回一个动态生成的类,我发现我不能pickle该类的实例:
>>> import pickle
>>> def f():
... class A: pass
... return A
...
>>> LocalA = f()
>>> la = LocalA()
>>> with open('testing.pickle', 'wb') as f:
... pickle.dump(la, f, pickle.HIGHEST_PROTOCOL)
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: Can't pickle local object 'f.<locals>.A'
这里发生了什么事?如果这很容易,为什么不使用此变通方法来实现允许对“本地对象”进行pickle的
转储方法呢?DerivedA
实例是可pickle的,因为DerivedA
通过匹配其完全限定名的全局变量可用,这就是pickle
在取消勾选时查找类的方式
尝试对本地类执行类似操作的问题在于,无法识别实例对应的类。如果您运行两次f
,您将得到两个A
类,并且无法判断哪个类应该是来自另一次运行的程序的未勾选A
实例类。如果你根本不运行f
,你就不会得到A
类,那么你对未点击实例的类型该怎么办?我想你没有仔细阅读。参考文件还明确指出,只有以下对象是可pickle的:
- 在模块顶层定义的函数(使用def,而不是>lambda)
- 在模块顶层定义的内置函数
- 在模块顶层定义的类
你的榜样
>>> def f():
... class A: pass
... return A
不在模块的顶层定义类,而是在f()
范围内定义类<代码>pickle
适用于全局类,而不是本地类。这会自动使可酸洗测试失败
DerivedA
是一个全局类,所以一切都很好
至于为什么只有顶级(对您来说是全局的)类和函数不能被pickle,参考文献也回答了这个问题(粗体):
请注意,函数(内置和用户定义)是通过“完全限定”名称引用进行pickle的,而不是通过值进行pickle的。这意味着只有函数名与定义函数的模块名一起被pickle函数的代码及其任何函数属性都不会被pickle。因此,定义模块必须在取消勾选环境中可导入,并且该模块必须包含命名对象,否则将引发异常
类似地,类通过命名引用进行pickle,因此在取消pickle环境中应用相同的限制
好了<代码>pickle
仅按名称引用序列化对象,而不是按对象中包含的原始指令序列化对象。这是因为pickle的
工作是序列化对象层次结构,而不是别的。我不同意,您可以同时pickle两者。您只需要使用更好的序列化程序,如dill
dill
(默认情况下)通过保存类定义而不是通过引用pickle来pickle类,因此它不会使您的第一个案例失败。如果愿意,您甚至可以使用dill
获取源代码
>>> import dill as pickle
>>> def f():
... class A: pass
... return A
...
>>> localA = f()
>>> la = localA()
>>>
>>> _la = pickle.dumps(la)
>>> la_ = pickle.loads(_la)
>>>
>>> class DerivedA(localA): pass
...
>>> da = DerivedA()
>>> _da = pickle.dumps(da)
>>> da_ = pickle.loads(_da)
>>>
>>> print(pickle.source.getsource(la_.__class__))
class A: pass
>>>
您只能pickle在模块顶层定义的类的实例 但是,如果将本地定义的类升级到顶级,则可以对其实例进行pickle 您必须设置本地类的_; qualname __; class属性。然后必须将该类分配给同名的顶级变量
def define_class(name):
class local_class:
pass
local_class.__qualname__ = name
return local_class
class_A = define_class('class_A') # picklable
class_B = define_class('class_B') # picklable
class_X = define_class('class_Y') # unpicklable, names don't match
好的,只有类的名称被pickle。我认为类本身被保存了(不知何故),然后这意味着保存它的基类,这是不可pickle的。顺便说一句,如果我在一个环境中取消pickle它,其中有一个不相关的类,但具有相同的名称,我将得到这个不相关类的一些frankstein monster对象,但具有旧类的属性?我认为这可以归结为:pickle对象并不像我想象的那样独立。@fonini也来自引用:“……当类实例被pickle时,它们的类的代码和数据不会随之被pickle。只有实例数据被pickle。”所以,是的,这听起来是正确的行为。:)我完全超越了这一点。我打赌这是关于聚焦不同的事物,不一定更好?还是dill在速度和存储对象的大小方面也更好?但是谢谢你提到它
dill
并不是更快,而且在存储对象的大小方面也不是更好。事实上,在这两方面都比pickle更糟糕。就序列化各种类型的对象的能力而言,它要好得多,这就是我的意思。您的解决方案似乎是适合我的方式。您上面的示例对我很有用,但当我尝试实际代码时,我不断得到“Can't pickle:它与XX.YY不是同一个对象”。你知道我做错了什么吗?那是我的错。。。。我正在创建一个class_A
的实例,然后在尝试pickle它之前重新定义这个类。
def define_class(name):
class local_class:
pass
local_class.__qualname__ = name
return local_class
class_A = define_class('class_A') # picklable
class_B = define_class('class_B') # picklable
class_X = define_class('class_Y') # unpicklable, names don't match