如果我使用派生的class,我可以"pickle local objects"?
I can "pickle local objects" if I use a derived class?
pickle
引用 states that 可以 pickle 的对象集相当有限。事实上,我有一个 returns 动态生成 class 的函数,我发现我无法 pickle class:
的实例
>>> 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
来说太复杂了。行。现在,神奇的是,如果我尝试 pickle 一个类似的对象,但是派生的 class,它起作用了!
>>> class DerivedA(LocalA): pass
...
>>> da = DerivedA()
>>> with open('testing.pickle', 'wb') as f:
... pickle.dump(da, f, pickle.HIGHEST_PROTOCOL)
...
>>>
这里发生了什么?如果这很容易,为什么 pickle
不使用此解决方法来实现允许 "local objects" 被腌制的 dump
方法?
DerivedA
实例是可腌制的,因为 DerivedA
可通过匹配其完全限定名称的全局变量获得,这就是 pickle
在取消腌制时查找 classes 的方式.
尝试用本地 classes 做这样的事情的问题是没有任何东西可以识别 which A
class 一个实例对应。如果你 运行 f
两次,你会得到两个 A
class,并且没有办法分辨哪个应该是 unpickled [=13] 的 class =] 来自另一个 运行 程序的实例。如果你根本不 运行 f
,你会得到 no A
classes,然后你到底在做什么处理 unpickled 实例的类型?
我想你没有仔细阅读the reference you cite。该参考文献还明确指出只有以下对象是可腌制的:
- functions defined at the top level of a module (using def, not >lambda)
- built-in functions defined at the top level of a module
- classes that are defined at the top level of a module
你的例子
>>> def f():
... class A: pass
... return A
不在模块的顶层定义 class,它在 f()
的 范围 内定义 class。 pickle
适用于 全球 classes,不适用于本地 classes。这会自动使 pickleable 测试失败。
DerivedA
是全局的 class,所以一切都很好。
至于为什么只有顶级(对你来说是全局的)classes 和函数不能被 pickle,参考文献也回答了这个问题(大胆的我的):
Note that functions (built-in and user-defined) are pickled by “fully qualified” name reference, not by value. This means that only the function name is pickled, along with the name of the module the function is defined in. Neither the function’s code, nor any of its function attributes are pickled. Thus the defining module must be importable in the unpickling environment, and the module must contain the named object, otherwise an exception will be raised.
Similarly, classes are pickled by named reference, so the same restrictions in the unpickling environment apply.
好了。 pickle
仅通过名称引用序列化对象,而不是通过对象中包含的原始指令。这是因为 pickle's
工作是序列化 对象层次结构 ,没有别的。
我不同意,你可以把两者都腌制。你只需要使用更好的序列化器,比如 dill
。 dill
(默认情况下)pickles classes 通过保存 class 定义而不是通过引用进行 pickling,所以它不会让你的第一个案例失败。如果愿意,您甚至可以使用 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
>>>
您只能选择在模块顶层定义的 classes 的实例。
但是,如果您将本地定义的 classes 的实例提升到顶层,则可以对其进行 pickle。
您必须设置本地 class 的 __ qualname__ class 属性。然后,您必须将 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
引用 states that 可以 pickle 的对象集相当有限。事实上,我有一个 returns 动态生成 class 的函数,我发现我无法 pickle class:
>>> 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
来说太复杂了。行。现在,神奇的是,如果我尝试 pickle 一个类似的对象,但是派生的 class,它起作用了!
>>> class DerivedA(LocalA): pass
...
>>> da = DerivedA()
>>> with open('testing.pickle', 'wb') as f:
... pickle.dump(da, f, pickle.HIGHEST_PROTOCOL)
...
>>>
这里发生了什么?如果这很容易,为什么 pickle
不使用此解决方法来实现允许 "local objects" 被腌制的 dump
方法?
DerivedA
实例是可腌制的,因为 DerivedA
可通过匹配其完全限定名称的全局变量获得,这就是 pickle
在取消腌制时查找 classes 的方式.
尝试用本地 classes 做这样的事情的问题是没有任何东西可以识别 which A
class 一个实例对应。如果你 运行 f
两次,你会得到两个 A
class,并且没有办法分辨哪个应该是 unpickled [=13] 的 class =] 来自另一个 运行 程序的实例。如果你根本不 运行 f
,你会得到 no A
classes,然后你到底在做什么处理 unpickled 实例的类型?
我想你没有仔细阅读the reference you cite。该参考文献还明确指出只有以下对象是可腌制的:
- functions defined at the top level of a module (using def, not >lambda)
- built-in functions defined at the top level of a module
- classes that are defined at the top level of a module
你的例子
>>> def f():
... class A: pass
... return A
不在模块的顶层定义 class,它在 f()
的 范围 内定义 class。 pickle
适用于 全球 classes,不适用于本地 classes。这会自动使 pickleable 测试失败。
DerivedA
是全局的 class,所以一切都很好。
至于为什么只有顶级(对你来说是全局的)classes 和函数不能被 pickle,参考文献也回答了这个问题(大胆的我的):
Note that functions (built-in and user-defined) are pickled by “fully qualified” name reference, not by value. This means that only the function name is pickled, along with the name of the module the function is defined in. Neither the function’s code, nor any of its function attributes are pickled. Thus the defining module must be importable in the unpickling environment, and the module must contain the named object, otherwise an exception will be raised.
Similarly, classes are pickled by named reference, so the same restrictions in the unpickling environment apply.
好了。 pickle
仅通过名称引用序列化对象,而不是通过对象中包含的原始指令。这是因为 pickle's
工作是序列化 对象层次结构 ,没有别的。
我不同意,你可以把两者都腌制。你只需要使用更好的序列化器,比如 dill
。 dill
(默认情况下)pickles classes 通过保存 class 定义而不是通过引用进行 pickling,所以它不会让你的第一个案例失败。如果愿意,您甚至可以使用 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
>>>
您只能选择在模块顶层定义的 classes 的实例。
但是,如果您将本地定义的 classes 的实例提升到顶层,则可以对其进行 pickle。
您必须设置本地 class 的 __ qualname__ class 属性。然后,您必须将 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