Pickle 动态参数化子类
我有一个通常存储腌制类类型的系统。
我希望能够以相同的方式保存动态参数化的类,但我不能,因为我在尝试腌制一个未全局找到的类(未在简单代码中定义)时收到 PicklingError 。
我的问题可以建模为以下示例代码:
class Base(object):
def m(self):
return self.__class__.PARAM
def make_parameterized(param_value):
class AutoSubClass(Base):
PARAM = param_value
return AutoSubClass
cls = make_parameterized(input("param value?"))
当我尝试腌制该类时,出现以下错误:
# pickle.PicklingError: Can't pickle <class '__main__.AutoSubClass'>: it's not found as __main__.AutoSubClass
import pickle
print pickle.dumps(cls)
我正在寻找某种方法将 Base 声明为 ParameterizedBaseClass ,该方法应定义所需的参数(上例中的PARAM
)。然后,动态参数化子类(上面的cls
)应该可以通过保存“ParameterizedBaseClass”类型和不同的参数值(上面的动态param_value
)来选择。
我确信在很多情况下,这是可以完全避免的......如果我真的(真的)必须的话,我也可以在我的代码中避免这种情况。我在某个时候(不要问)正在玩 __metaclass__
、copyreg
甚至 __builtin__.issubclass
,但无法破解这个。
我觉得如果我不问:如何以相对干净的方式实现这一点,我就不会真正忠于Python精神。
I have a system which commonly stores pickled class types.
I want to be able to save dynamically-parameterized classes in the same way, but I can't because I get a PicklingError on trying to pickle a class which is not globally found (not defined in simple code).
My problem can be modeled as the following example code:
class Base(object):
def m(self):
return self.__class__.PARAM
def make_parameterized(param_value):
class AutoSubClass(Base):
PARAM = param_value
return AutoSubClass
cls = make_parameterized(input("param value?"))
When I try to pickle the class, I get the following error:
# pickle.PicklingError: Can't pickle <class '__main__.AutoSubClass'>: it's not found as __main__.AutoSubClass
import pickle
print pickle.dumps(cls)
I am looking for some method to declare Base as a ParameterizableBaseClass
which should define the params needed (PARAM
in above example). A dynamic parameterized subclass (cls
above) should then be picklable by saving the "ParameterizableBaseClass" type and the different param-values (dynamic param_value
above).
I am sure that in many cases, this can be avoided altogether... And I can avoid this in my code as well if I really (really) have to. I was playing with __metaclass__
, copyreg
and even __builtin__.issubclass
at some point (don't ask), but was unable to crack this one.
I feel like I wouldn't be true to the python spirit if I wasn't to ask: how can this be achieved, in a relatively clean way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我知道这是一个非常古老的问题,但我认为值得分享一种比当前接受的解决方案(使参数化类成为全局类)更好的腌制参数化类的方法。
使用 __reduce__ 方法,我们可以提供一个可调用函数,它将返回所需类的未初始化实例。
值得阅读
__reduce__
文档几次才能确切地了解这里发生了什么。希望有人觉得这很有用。
I know this is a very old question, but I think it is worth sharing a better means of pickling the parameterised classes than the one that is the currently accepted solution (making the parameterised class a global).
Using the
__reduce__
method, we can provide a callable which will return an uninitialised instance of our desired class.It is worth reading the
__reduce__
docs a couple of times to see exactly what is going on here.Hope somebody finds this useful.
是的,这是可能的 -
每当您想要为对象自定义 Pickle 和 Unpickle 行为时,您只需在对象上设置“
__getstate__
”和“__setstate__
”方法即可。类本身。在这种情况下,有点棘手:
正如您所观察到的,需要在全局命名空间中存在一个类,该类是当前正在腌制的对象的类:它必须是同一个类,具有相同的名称。好的 - 协议是 gthis 存在于全局名称空间中的类可以在 Pickle 时创建。
在 Unpickle 时,具有相同名称的类必须存在 - 但它不必是同一个对象 - 只是表现得像它那样 - 并且当在 Unpickling 过程中调用
__setstate__
时,它可以重新创建原始对象的参数化类,并通过设置对象的 __class__ 属性将其自己的类设置为该类。设置对象的 __class__ 属性可能会令人反感,但这就是 OO 在 Python 中的工作方式,并且有官方文档记录,甚至可以跨实现工作。 (我在 Python 2.6 和 Pypy 中测试了这个片段)
Yes, it is possible -
Whenever you want to custom the Pickle and Unpickle behaviors for your objects, you just have to set the "
__getstate__
" and "__setstate__
" methods on the class itself.In this case it is a bit trickier:
There need, as you observed - to exist a class on the global namespace that is the class of the currently being pickled object: it has to be the same class, with the same name. Ok - the deal is that gthis class existing in the globalname space can be created at Pickle time.
At Unpickle time the class, with the same name, have to exist - but it does not have to be the same object - just behave like it does - and as
__setstate__
is called in the Unpickling proccess, it can recreate the parameterized class of the orignal object, and set its own class to be that one, by setting the__class__
attribute of the object.Setting the
__class__
attribute of an object may seen objectionable but it is how OO works in Python and it is officially documented, it even works accross implementations. (I tested this snippet in both Python 2.6 and Pypy)我想现在已经太晚了,但是 pickle 是一个我宁愿避免任何复杂的模块,因为它有这样的问题以及更多的问题。
无论如何,既然 pickle 想要在全局中使用该类,它就可以拥有它:
但是,是的,你可能把事情变得过于复杂了。
I guess it's too late now, but pickle is a module I'd rather avoid for anything complex, because it has problems like this one and many more.
Anyways, since pickle wants the class in a global it can have it:
But yeah, you're probably overcomplicating things.
不是在模块顶层创建的类不能被 pickle,如 Python 文档中所示。
此外,即使对于顶级模块类的实例,也不会存储类属性。因此,在您的示例中
PARAM
无论如何都不会被存储。 (上面链接的 Python 文档部分也有解释)Classes that are not created in the top level of a module cannot be pickled, as shown in the Python documentation.
Furthermore, even for an instance of a top level module class the class attributes are not stored. So in your example
PARAM
wouldn't be stored anyway. (Explained in the Python documentation section linked above as well)通过使用自定义元类并在其上使用copyreg 可以实现这一点。这样,您就可以pickle任何自定义动态参数化子类。
问题 7689 对此进行了描述和实现。
示范:
It is possible by having a custom metaclass and using
copyreg
on it. That way, you can pickle any custom dynamically parameterized sub-classes.This is described and implemented in issue 7689.
Demonstration: