在C++编写的Python扩展中添加pickle
我用C语言写了一个Python扩展,运行得很好,没有问题。现在我想和它一起使用pickle。看文档的时候我有点困惑。我按照这里的建议写了一个测试用的__reduce__
函数,专门针对“扩展”:
http://docs.python.org/library/pickle.html#the-pickle-protocol
我不太明白“可调用对象”应该是什么。我试过用PyObject_Type(self)
的结果。这个是“可调用的”,基本上能用,但当对象被反序列化(unpickle)时,它会调用__init__
,这让我遇到了一些麻烦。
有没有什么标准的方法可以让一个可调用对象只调用__new__
方法,避免类的初始化呢?
1 个回答
如果你调用了 __new__
方法,并且使用的是 type 作为元类,那么如果 __new__
的结果是你所定义类型的子类,就会调用 __init__
方法。换句话说,假设你有一个类叫 Foo。如果你调用 PyObject_Type(self)
的结果,这就相当于调用 Foo()
。这意味着 Foo.__new__
会被调用,如果返回的结果是 Foo 的子类,那么 __init__
也会被调用。
再深入一点,当你调用 Foo() 时,实际上是在调用 type_call(在 typeobject.c 文件中),这个过程会先执行 tp_new,然后是 tp_init。如果你直接提供你对象的 new 函数(比如用 Foo_new() 而不是 PyObject_Type(self)),那么你就是在调用 __new__
而没有调用 __init__
,这样你就能得到你想要的结果。(别忘了在调用 __new__
时把 Foo 作为参数提供)。
所以,最后回答你的问题,你可以简单地调用 Foo.__new__(Foo, ...)
。这里有一段代码可以实现你想要的功能。
class Foo(object):
def __new__(cls):
return super(Foo, cls).__new__(cls)
def __init__(self):
print "__init__"
def __reduce__(self):
return (Foo.__new__, (Foo, ))
print "one"
x = Foo() # prints __init__
print "two"
y = Foo.__new__(Foo) # does not print __init__
print "three"
import pickle
p = pickle.dumps(Foo)
z = pickle.loads(p) # does not print __init__
顺便提一下,当我试图搞清楚这一切的时候,我发现几乎所有情况下,我都可以实现我的代码,并且让 __init__
被调用。我的错误在于没有把 __reduce__
元组的第三个参数的内容分开。第二个参数可以不提供(只要 __init__
能接受),而在第三个参数中提供一些内容来直接更新 __dict__
是完全可以的。如果你查看 cpython 源代码中的 Modules 和 Objects 目录,你会看到许多 __reduce__
的实现都是这样工作的。