问题
I have an Event
class defined in C++ that I expose to Python using Boost. My scripts are expected to derive from this class, and I'd like to do some initialization whenever a new child class is defined.
How can I set the metaclass of the exposed Event
class such that whenever a Python script derives from this class, the metaclass could do the required initialization?
I would like to avoid having to explicitly use a metaclass in the scripts...
class KeyboardEvent(Event): # This is what I want
pass
class KeyboardEvent(Event, metaclass=EventMeta): # This is not a good solution
pass
Edit: Part of the solution
It seems there's no way to set the metaclass with Boost.Python. The next best thing is to improvise and change the metaclass after the class was defined. In native Python, the safe way to change a metaclass is to do this:
B = MetaClass(B.__name__, B.__bases__, B.__dict__)
In Boost, it'd look something like this:
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__"]));
}
The problem is that I can't seem to find a way to define a metaclass with Boost.Python, which is why I've opened How to define a Python metaclass with Boost.Python?.
回答1:
If boost does not offer a way to do it from withn c++, and it looks like it don't, the way to go is to create wrapper classes that implement the metaclass -
It can be done more or less automatically usign a ittle bit of instrospection. Let's suppose your boost module is named "event" - you should either name the file as _event or place it inside you module, and write an python file - named "event.py" (or an __init__.py
file on your module that would do more or less this:
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
Thos cpde will automatically set module variables equal to any names found in the native"_event" module - and for each class it encounters, create a new class changing the metaclass, as in your example.
It may be that you get a metaclass conflict by doing this. If so, the way is to make the newly created classes to be proxies to the native classes, by creating proper __getattribute__
and __setattr__
methods. Just ask in a comment if you will need to do that.
来源:https://stackoverflow.com/questions/9037571/setting-metaclass-of-wrapped-class-with-boost-python