将pickle添加到用C ++编写的Python扩展中

时间:2012-01-02 23:32:58

标签: c++ python c pickle

我在C中编写了一个Python扩展。它有效,没问题。现在我想用它来泡菜。从文档我很困惑。我在这里为“扩展”编写了一个测试__reduce__函数:

http://docs.python.org/library/pickle.html#the-pickle-protocol

我想念的是“可调用对象”应该是什么。我试过了PyObject_Type(self)的结果。这是“可调用的”并且基本上可以工作,但是当对象被打开时,它会调用__init__,这给我带来了一些麻烦。

有没有标准的方法来调用只调用__new__方法的callable,避免类初始化?

1 个答案:

答案 0 :(得分:2)

如果您致电__new__,并且您使用类型作为元类,则__init__的结果将是__new__的类型的子类型。换句话说,让我们说你有一个Foo课。如果您调用PyObject_Type(self)的结果,则与调用Foo()相同。这意味着将调用Foo。__new__,如果返回值是Foo的子类型,则将调用__init__

深入挖掘,当你调用Foo()时,它真正调用type_call(在typeobject.c中),这就是tp_new后跟tp_init的内容。如果您直接提供对象的新函数(例如Foo_new()而不是PyObject_Type(self)),那么您在不调用__new__的情况下调用__init__并且可以获得您的&{ #39;重新寻找。 (别忘了提供Foo作为__new__的论据。)

因此,要最终回答您的问题,您只需致电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__接受了这一点)并在第3个参数中提供了一堆将直接更新__dict__的内容。如果您查看模块和对象目录中的cpython源代码,您将看到__reduce__的许多实现,所有这些都以这种方式工作。