Python Pickle动态参数化的子类

Python Pickle动态参数化的子类,python,class,dynamic,factory,pickle,Python,Class,Dynamic,Factory,Pickle,我有一个系统,它通常存储pickle类类型 我希望能够以同样的方式保存动态参数化的类,但我不能,因为我在尝试pickle一个未全局找到(未在简单代码中定义)的类时遇到PicklingError 我的问题可以建模为以下示例代码: class Base(object): def m(self): return self.__class__.PARAM def make_parameterized(param_value): class AutoSubClass(Base): PARAM

我有一个系统,它通常存储pickle类类型

我希望能够以同样的方式保存动态参数化的类,但我不能,因为我在尝试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时,出现以下错误:

# pickle.PicklingError: Can't pickle <class '__main__.AutoSubClass'>: it's not found as __main__.AutoSubClass
import pickle
print pickle.dumps(cls)
#pickle.PicklingError:无法pickle:找不到它作为_umain__u; AutoSubClass
进口泡菜
打印pickle.dumps(cls)
我正在寻找一些方法来将Base声明为
ParameterableBaseClass
,它应该定义所需的参数(
PARAM
)。然后,应通过保存“ParameterableBaseClass”类型和不同的参数值(上面的dynamic
param_值
)来选择动态参数化子类(
cls

我相信在很多情况下,这是完全可以避免的。。。如果我真的(真的)必须的话,我也可以在我的代码中避免这种情况。我在玩
\uuuuuuuuuuuuuuuuuu元类
copyreg
,甚至是
\uuuuuuuuuuuuuuuuuu内置类
,但我无法破解这个


我觉得如果我不去问:如何以一种相对干净的方式实现这一点?

不在模块顶层创建的类不能被pickle


此外,即使对于顶级模块类的实例,也不会存储类属性。因此,在您的示例中,
PARAM
无论如何都不会被存储。(在上面链接的Python文档部分也进行了解释)

我想现在已经太晚了,但是对于任何复杂的东西,pickle都是一个我宁愿避免使用的模块,因为它有类似于此的问题以及更多的问题

无论如何,由于pickle希望类处于全局状态,因此它可以拥有它:

import cPickle

class Base(object):
    def m(self):
        return self.__class__.PARAM

    @classmethod
    def make_parameterized(cls,param):
        clsname = "AutoSubClass.%s" % param
        # create a class, assign it as a global under the same name
        typ = globals()[clsname] = type(clsname, (cls,), dict(PARAM=param))
        return typ

cls = Base.make_parameterized('asd')

import pickle
s = pickle.dumps(cls)

cls = pickle.loads(s)
print cls, cls.PARAM
# <class '__main__.AutoSubClass.asd'> asd
导入cPickle
类基(对象):
def m(自我):
返回self.\u类\u.PARAM
@类方法
def使_参数化(cls,参数):
clsname=“自动子类。%s”%param
#创建一个类,将其指定为同名的全局类
typ=globals()[clsname]=type(clsname,(cls,),dict(PARAM=PARAM))
返回类型
cls=Base.make_参数化('asd')
进口泡菜
s=pickle.dumps(cls)
cls=酸洗负荷
打印cls,cls.PARAM
#自闭症
但是,是的,你可能把事情复杂化了。

是的,这是可能的-

每当您想要自定义对象的Pickle和Unpickle行为时,您只需在类本身上设置“
\uuuu getstate\uuuu
”和“
\uuu setstate\uuuu
”方法

在这种情况下,它有点棘手: 正如您所观察到的,需要在全局命名空间上存在一个类,该类是当前被pickle对象的类:它必须是相同的类,具有相同的名称。好的-交易是可以在Pickle时创建globalname空间中存在的这个类

在取消勾选时,具有相同名称的类必须存在-但它不必是相同的对象-只需像它那样行为-并且在取消勾选过程中调用
\uuuuu setstate\uuuuu
,它可以重新创建原始对象的参数化类,并将自己的类设置为该类,通过设置对象的
\uuuu class\uuuu
属性

设置对象的
\uuuuu class\uuuuuu
属性可能会让人觉得不舒服,但这正是OO在Python中的工作方式,并且有官方文档记录,它甚至可以在跨实现中工作。(我在Python2.6和Pypy中测试了这个片段)


我知道这是一个非常古老的问题,但我认为值得分享一种更好的方法来处理参数化类,而不是目前公认的解决方案(使参数化类成为全局类)

通过使用
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法,我们可以提供一个可调用函数,它将返回我们所需类的未初始化

class Base(object):
    def m(self):
        return self.__class__.PARAM

    def __reduce__(self):
        return (_InitializeParameterized(), (self.PARAM, ), self.__dict__)


def make_parameterized(param_value):
    class AutoSub(Base):
        PARAM = param_value
    return AutoSub


class _InitializeParameterized(object):
    """
    When called with the param value as the only argument, returns an 
    un-initialized instance of the parameterized class. Subsequent __setstate__
    will be called by pickle.
    """
    def __call__(self, param_value):
        # make a simple object which has no complex __init__ (this one will do)
        obj = _InitializeParameterized()
        obj.__class__ = make_parameterized(param_value)
        return obj

if __name__ == "__main__":

    from pickle import dumps, loads

    a = make_parameterized("a")()
    b = make_parameterized("b")()

    print a.PARAM, b.PARAM, type(a) is type(b)
    a_p = dumps(a)
    b_p = dumps(b)

    del a, b
    a = loads(a_p)
    b = loads(b_p)

    print a.PARAM, b.PARAM, type(a) is type(b)
这本书值得读几遍,看看这里到底发生了什么


希望有人觉得这很有用。

用一种相对干净的方式?我认为对“干净”的任何合理定义都是不可能的。为什么要尝试pickle在运行时生成类的对象?实际用例是什么?你要如何去解开那些类不存在的东西?我也不明白这个用例。为什么不能在初始化后返回一个带有该参数作为属性的类实例呢。另一个注释是,您还应该避免使用python魔法,即
self.\uuuu class\uuuu.PARAM
。我认为你把这件事复杂化了。可能情况是,这件事太复杂了(在这种情况下,问题可能仍然存在,即使不考虑我的情况)。我的例子看起来可能真的很愚蠢——它只是一个例子。我的真实系统将类类型和创建参数一起pickle,并在并行处理场景中在进程之间传递它们。除了使用大约20个不同的类,每个类都有特定的代码,我还有一些“just choose your parameter”类。我希望能够动态地对它们进行子类化,而无需更改(并可能重新加载)代码,并使它们可拾取。HTH.@Lennart:“你打算如何取消勾选不存在类的对象?”-通过在取消勾选时间创建类!:-)-请看我下面的回答。@jsbueno:我不得不说,在这种情况下,感觉整个动态类创建都可以跳过,而且仍然可以工作。你知道名字,参数是动态的吗?好吧,你不需要为此创建一个类…谢谢你的回复!确实,随意使用
pickle.dump
不会同意pickle我的类类型。我正在寻找一种机制(例如
\uuuuu reduce\uuuuuu
\uuuu setstate\uuuuuu
或一些基类或元类构造中的类似机制),它允许我pickle元组des
class Base(object):
    def m(self):
        return self.__class__.PARAM

    def __reduce__(self):
        return (_InitializeParameterized(), (self.PARAM, ), self.__dict__)


def make_parameterized(param_value):
    class AutoSub(Base):
        PARAM = param_value
    return AutoSub


class _InitializeParameterized(object):
    """
    When called with the param value as the only argument, returns an 
    un-initialized instance of the parameterized class. Subsequent __setstate__
    will be called by pickle.
    """
    def __call__(self, param_value):
        # make a simple object which has no complex __init__ (this one will do)
        obj = _InitializeParameterized()
        obj.__class__ = make_parameterized(param_value)
        return obj

if __name__ == "__main__":

    from pickle import dumps, loads

    a = make_parameterized("a")()
    b = make_parameterized("b")()

    print a.PARAM, b.PARAM, type(a) is type(b)
    a_p = dumps(a)
    b_p = dumps(b)

    del a, b
    a = loads(a_p)
    b = loads(b_p)

    print a.PARAM, b.PARAM, type(a) is type(b)