在不调用 __init__ 的情况下初始化 PyObject

Initialize PyObject without calling __init__

我有一个包装器 class,如下例所示,用 Python.

编写
class Foo:
    def __init__(self, bar=None):
        if bar:
            self._bar = bar
        else:
            self._bar = CreateBarObject("value") 

我通过 Python C API

创建了一个实例
// c++ pseudo code for convenience

auto obj = PyObject_CallObject(Foo) 
auto bar = CreateBarObject("another_value");
PyObject_SetAttrString(obj, "_bar", bar)

正如您从代码中看到的,Foo.__init__ 将在创建一个创建新 bar 对象的实例时被调用。但是我想绕过这个 "heavy" 操作。那么有什么安全的方法可以创建 Foo 的实例,这样我就可以通过 Python C API 设置 self._bar?有什么想法吗?

self.bar 设为 属性,在第一次需要时创建实例。

class Foo:

    def __init__(self, bar=None):
        self._bar = bar

    @property
    def bar(self):
        if self._bar is None:
            self._bar = CreateBarObject(...)
        return self._bar

实例功能齐全。实例的 bar 属性 将在第一次需要时创建 Bar 对象。或者你可以在这之前从 C API 设置 _bar,然后使用它。或者您可以在实例化 class 时传入现有的 Bar 对象。你的选择。

你应该可以直接调用Foothe tp_new(相当于在Python层调用Foo.__new__)。这将执行分配和 C 级 "mandatory initialization" 工作,而没有 tp_init/__init__ 的 "optional initialization" 工作。这与 pickle 用于创建类型实例而不初始化它们的策略相同(因此它可以通过 __setstate__ 或直接填充它们 __dict__,视情况而定)。

假设 Foo 是 C 级 PyTypeObject*,这样的事情应该可以解决问题:

auto emptytup = PyTuple_New(0);  /* AFAICT, the args tuple is mandatory, but kwargs is optional */
/* Error check for PyTuple_New failure */
auto obj = Foo->tp_new(Foo, emptytup, NULL);
Py_DECREF(emptytup);
/* Error check for new fail */
auto bar = CreateBarObject("another_value");
PyObject_SetAttrString(obj, "_bar", bar)