Инициализировать PyObject без вызова __init__

1

У меня есть класс-оболочка, как в примере ниже, написанном на 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? Есть идеи?

  • 1
    Почему вы хотите установить _bar через C API? Почему бы просто не передать объект bar в Foo при первом вызове?
  • 0
    Foo() без аргументов должен создать self._bar для работы экземпляра. Но этот вызов - очень дорогая операция, поэтому я хотел бы обойти эту стоимость плюс обход ненужных инструкций байтового кода, это должна быть очень легкая инициализация, если это возможно
Показать ещё 5 комментариев
Теги:
python-internals
cpython

2 ответа

2
Лучший ответ

Вы должны иметь возможность напрямую вызывать 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)
-2

Сделайте 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 при создании экземпляра класса. Твой выбор.

  • 0
    Поскольку вызов __init__ и if bar оказывают существенное влияние на производительность по крайней мере для этого класса, я бы хотел избавиться от выполнения байт-кода Python в этом случае. Но для общего подхода это действительно хорошая идея, никогда не задумываясь об этом
  • 0
    Тогда вы можете просто покончить с __init__ и добавить атрибут класса _bar = None . Это покончит со всем байт-кодом при инициализации Foo .
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню