使用Boost.Python设置包装类的元类

5 投票
1 回答
555 浏览
提问于 2025-04-17 11:14

我在C++中定义了一个叫做Event的类,并通过Boost把它暴露给Python。我的脚本需要从这个类派生出新的子类,每当定义一个新的子类时,我想做一些初始化工作。

我该如何设置这个暴露出来的Event类的元类,以便每当Python脚本从这个类派生时,元类能够进行所需的初始化呢?

我希望在脚本中避免明确使用元类……

class KeyboardEvent(Event):  # This is what I want
    pass

class KeyboardEvent(Event, metaclass=EventMeta): # This is not a good solution
    pass

编辑: 部分解决方案

看起来在Boost.Python中没有办法直接设置元类。下一个最好的办法是临时处理一下,在类定义后再更改元类。在原生Python中,安全地更改元类的方法是这样的:

B = MetaClass(B.__name__, B.__bases__, B.__dict__)

在Boost中,它看起来会是这样的:

BOOST_PYTHON_MODULE(event)
{
    using namespace boost::python;
    using boost::python::objects::add_to_namespace;

    class_<EventMetaClass> eventmeta("__EventMetaClass")
        ...;

    class_<Event> event("Event")
        ...;

    add_to_namespace(scope(), "Event",
        eventmeta(event["__name__"], event["__bases__"], event["__dict__"]));
}

问题是我似乎找不到在Boost.Python中定义元类的方法,这就是我为什么提出了这个问题:如何在Boost.Python中定义Python元类?.

1 个回答

0

如果 Boost 没有提供从 C++ 内部实现的方式,而看起来确实没有,那你可以考虑创建一些包装类来实现元类。

这可以通过一点点自省(也就是程序自己查看自己)来做到,基本上是自动化的。假设你的 Boost 模块叫做 "event",你应该把文件命名为 _event,或者把它放在你的模块里面,然后写一个 Python 文件,命名为 "event.py"(或者在你的模块里写一个 __init__.py 文件,内容大致如下:

import _event

class eventmeta(type):
    ...

event_dict = globals()
for key, value in _event.__dict__.items():
    if isinstance(value, type):
        event_dict[key] = eventmeta(key, (value,),{})
    else:
        #set other module members as members of this module
        event_dict[key] = value

del key, value, event_dict

这段代码会自动将模块变量设置为在原生的 "_event" 模块中找到的任何名称,并且对于它遇到的每个类,创建一个新的类,改变元类,就像你在例子中所展示的那样。

这样做可能会导致元类冲突。如果发生这种情况,你可以让新创建的类成为原生类的代理,通过创建合适的 __getattribute____setattr__ 方法来实现。如果你需要这样做,可以在评论中问我。

撰写回答