Search code examples
pythonccpythonpython-internals

Initialize PyObject without calling __init__


I have a wrapper class as in the example below, written in Python.

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

I create an instance via the Python C API

// c++ pseudo code for convenience

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

As you can see from the code, Foo.__init__ will be called when an instance gets created which creates a new bar object. But I would like to bypass this "heavy" operation. So any safe way to create an instance of Foo so I can set self._bar via the Python C API? Any ideas?


Solution

  • You should be able to directly invoke the tp_new of Foo (equivalent to invoking Foo.__new__ at the Python layer). That will perform the allocation and C level "mandatory initialization" work, without the "optional initialization" work of tp_init/__init__. It's the same strategy pickle uses to create instances of a type without initializing them (so it can fill them in via __setstate__ or directly filling in __dict__, as appropriate).

    Assuming Foo is a C level PyTypeObject*, something like this should do the trick:

    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)