boost::python:如何覆盖静态属性?

Posted

技术标签:

【中文标题】boost::python:如何覆盖静态属性?【英文标题】:boost::python: How can I override a static property? 【发布时间】:2020-01-21 21:36:46 【问题描述】:

我有一个基类和一个派生类,它们都有一个同名的静态方法。是否可以在保持名称相同的同时公开它们?这会编译,但在导入模块时会引发异常。

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;

这将为我提供属性,但如果静态方法不是常量,则在一般情况下不起作用:

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();

【问题讨论】:

什么是“未报告的异常”? (例如,您可以使用PyErr_WriteUnraisable。) 【参考方案1】:

查看Boost Python 如何暴露静态属性以及它在Python 中的行为方式,看起来这是Boost Python 中的一个错误。

当我尝试导入有问题的示例时出现以下错误:

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

无法设置属性,因为派生类中存在冲突。 Derived 类首先从 Base 继承 say_hi 属性,然后尝试定义它自己的。这可以通过将Derived 类中的say_hi 更改为say_hello 来轻松验证,正如作者所做的那样:

py::class_&lt;Derived, py::bases&lt;Base&gt;&gt;("Derived").add_static_property("say_hello", &amp;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)

Derived 中删除say_hi 后:

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)

【讨论】:

Boost.Python 的静态属性与 Python 的属性不同。前者解析类及其实例,而后者仅解析其实例并为类返回自身。例如,Base.say_hi 将为前者返回 hi,并为后者返回一个 property 实例,等等。也就是说,一个简单的解决方案是直接将属性绑定到派生类以避免任何类型的解决问题。我已经发布了一个带有示例的答案。【参考方案2】:

问题在于,当 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

【讨论】:

以上是关于boost::python:如何覆盖静态属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何覆盖父对象的静态属性并让父对象访问 PHP 中的新值?

Javascript es6覆盖静态属性

java覆盖和隐藏

如何使用Powermock对静态方法进行mock

如何将静态 UIButton 覆盖在 UITableView 上

如何将静态 UIButton 覆盖在 UITableView 上