boost :: python: Как я могу переопределить свойство stati c? - PullRequest
1 голос
/ 22 января 2020

У меня есть базовый класс и производный класс, оба из которых имеют метод с одинаковым именем stati c. Возможно ли выставить их обоих, оставив имена одинаковыми? Это компилирует, но выдает исключение при импорте модуля.

struct Base
{
    static std::string say_hi() { return "Hi"; }
};

struct Derived : public Base
{
    static std::string say_hi() { return "Hello"; }
};

BOOST_PYTHON_MODULE(HelloBoostPython)
{
    namespace py = boost::python;

    py::class_<Base>("Base").add_static_property("say_hi", &Base::say_hi);

    py::class_<Derived, py::bases<Base>>("Derived").add_static_property("say_hi", &Derived::say_hi);
}

При импорте:

>>> import HelloBoostPython
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
SystemError: initialization of HelloBoostPython raised unreported exception

Изменение последней строки на другое имя работает, но я бы предпочел свойство базового класса должно быть переопределено:

py::class_<Derived, py::bases<Base>>("Derived").add_static_property("say_hello", &Derived::say_hi);

Это работает, но вместо свойств я получаю методы класса:

BOOST_PYTHON_MODULE(HelloBoostPython)
{
    namespace py = boost::python;

    py::object base = py::class_<Base>("Base");
    base.attr("say_hi") = Base::say_hi;

    py::object derived = py::class_<Derived, py::bases<Base>>("Derived");
    derived.attr("say_hi") = Derived::say_hi;
}

Это даст мне свойства, но не будет работать в общем случай, если методы stati c не являются постоянными:

BOOST_PYTHON_MODULE(HelloBoostPython)
{
    namespace py = boost::python;

    py::object base = py::class_<Base>("Base");
    base.attr("say_hi") = Base::say_hi();

    py::object derived = py::class_<Derived, py::bases<Base>>("Derived");
    derived.attr("say_hi") = Derived::say_hi();
}

Hmm

Ответы [ 2 ]

0 голосов
/ 27 января 2020

Проблема в том, что когда метакласс Boost. Python пытается объявить и установить Derived.say_hi, он разрешается в Base.say_hi.__set__, потому что это существующий дескриптор класса, унаследованный от Base. Решением может быть привязка свойства к вашему классу Derived самостоятельно. Например,

namespace py = boost::python;

py::class_<Base>("Base").add_static_property("say_hi", &Base::say_hi);

py::class_<Derived, py::bases<Base>> Derived_("Derived");

PyDict_SetItemString(
    py::downcast<PyTypeObject>(Derived_.ptr())->tp_dict,
    "say_hi",
    Derived_.attr("__base__").attr("__dict__")["say_hi"].attr("__class__")(
        py::make_function(&Derived::say_hi)
    ).ptr()
);

Таким образом say_hi напрямую связывается с классом Derived как экземпляр Boost.Python.StaticProperty. Теперь следующий код:

print(Base().say_hi, Base.say_hi, Derived().say_hi, Derived.say_hi)

Будет правильно печатать:

Hi Hi Hello Hello
0 голосов
/ 23 января 2020

Глядя на то, как Boost Python выставляет свойство stati c и как оно ведет себя в Python, похоже, что это ошибка в Boost Python.

Я получаю следующую ошибку, когда я попробуйте импортировать проблемный пример c:

>>> import HelloBoostPython
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>>

Атрибут не может быть установлен, поскольку в производном классе есть cla sh. Класс Derived сначала наследует свойство say_hi от Base, а затем пытается определить свое собственное. Это можно легко проверить, изменив say_hi на say_hello в классе Derived, как это было сделано автором:

py::class_<Derived, py::bases<Base>>("Derived").add_static_property("say_hello", &Derived::say_hi);

Затем мы можем увидеть следующие дескрипторы данных help(HelloBoostPython.Derived):

    class Derived(Base)
...
...
     |  ----------------------------------------------------------------------
     |  Data descriptors defined here:
     |
     |  say_hello
     |
     |  ----------------------------------------------------------------------
     |  Data and other attributes defined here:
     |
     |  __instance_size__ = 24
     |
     |  ----------------------------------------------------------------------
     |  Data descriptors inherited from Base:
     |
     |  say_hi
...
...

Однако, когда мы определяем подобное в python:

class Base(object):

    def get_say_hi(self):
        return "hi"

    say_hi = property(get_say_hi)

class Derived(Base):

    def get_say_hi(self):
        return "hello"

    say_hi = property(get_say_hi)

Мы можем видеть, что здесь у нас есть только say_hi, определенное в Derived:

class Derived(Base)
...
...
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  say_hi
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from Base:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)
(END)

И после удаления say_hi из Derived:

class Base(object):

    def get_say_hi(self):
        return "hi"

    say_hi = property(get_say_hi)

class Derived(Base):
    None

Мы получаем его по наследству от Base, затем:

class Derived(Base)
...
...
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from Base:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)
 |
 |  say_hi
(END)
...