У меня есть класс-оболочка, как в примере ниже, написанном на Python.
class Foo:
def __init__(self, bar=None):
if bar:
self._bar = bar
else:
self._bar = CreateBarObject("value")
Я создаю экземпляр через API Python C
// c++ pseudo code for convenience
auto obj = PyObject_CallObject(Foo)
auto bar = CreateBarObject("another_value");
PyObject_SetAttrString(obj, "_bar", bar)
Как вы можете видеть из кода, Foo.__init__
будет вызываться, когда создается экземпляр, создающий новый объект bar
. Но я хотел бы обойти эту "тяжелую" операцию. Итак, любой безопасный способ создать экземпляр Foo, чтобы я мог установить self._bar
через API Python C? Есть идеи?
Вы должны иметь возможность напрямую вызывать tp_new
из Foo
(эквивалентно Foo.__new__
на уровне Python). Это будет выполнять работу по распределению и принудительной инициализации уровня C, без "необязательной инициализации" работы tp_init
/__init__
. В той же стратегии pickle
использует для создания экземпляров типа без их инициализации (чтобы он мог заполнить их через __setstate__
или непосредственно заполнить __dict__
, если это необходимо).
Предполагая, что Foo
- это уровень PyTypeObject*
уровня 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)
Сделайте 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
в первый раз, когда он понадобится. Или вы можете установить _bar
из C API до этого, и это будет использоваться вместо этого. Или вы можете передать существующий объект Bar
при создании экземпляра класса. Твой выбор.
__init__
и if bar
оказывают существенное влияние на производительность по крайней мере для этого класса, я бы хотел избавиться от выполнения байт-кода Python в этом случае. Но для общего подхода это действительно хорошая идея, никогда не задумываясь об этом
__init__
и добавить атрибут класса _bar = None
. Это покончит со всем байт-кодом при инициализации Foo
.
_bar
через C API? Почему бы просто не передать объектbar
вFoo
при первом вызове?Foo()
без аргументов должен создатьself._bar
для работы экземпляра. Но этот вызов - очень дорогая операция, поэтому я хотел бы обойти эту стоимость плюс обход ненужных инструкций байтового кода, это должна быть очень легкая инициализация, если это возможно