从c ++捕获boost :: python-wrapped类的实例属性 [英] Catch creation of instance attributes of boost::python-wrapped classes from c++
问题描述
我使用boost :: python包装(许多)c ++类。如果我从一个python脚本分配属性名称时,键入属性名称,python默默地创建新的实例属性,这从来不是我缩进。有没有办法捕获这样的事件(并引发异常?)?
我已经看到了一些关于主题的网站上的帖子,但没有一个似乎给出确定的答案;我试图覆盖 __ setattr __
&朋友,但我不能得到它的权利;
p>
__ setattr __
实际上是要走的路。如果你担心性能,我认为python仍然在内部执行这个调用,所以重写函数不应该再花费。 __ setattr __
的问题是,如果你想保持你的实现在你的c ++代码,它可能是棘手的实现。 这是一个python版本:
#在C ++库中定义MyClassBase
class MyClass
def __init __(self):
self.test1 ='test1'
self .__ initialized = True
def __setattr __(self,name,value):
self .__ dict __。has_key('_ MyClass__initialized')或self .__ dict __。has_key(name):
object .__ setattr __(self,name,value)
else:
raise AttributeError s不存在。%name)
def main():
o = MyClass()
print o.test1
o.test1 ='test1_set'
print o.test1
#这将抛出
o.test2 ='test2_set'
如果__name__ =='__main__':
main()
这里有一种使用元类的方法。这个方法的优点是你只需要定义 __ setattr __
函数,然后你可以使用你的inject类定义每个类:
#创建一个注入器元类来添加功能到
#我们的模块,你可以重用BoostPythonMetaclass
#all boost :: python-export对象
BoostPythonMetaclass = MyClass .__ class__
class injector(object):
class __metaclass __(BoostPythonMetaclass):
def __init __(self,name,bases,dict):
对于基数b:
如果类型(b)不在(self,类型):
for k,v in dict.items():
setattr(b,k,v)
setattr(b,'__initialized',True)
返回类型.__ init __(self,name,bases,dict)
def __setattr __(self,name,value):
if not self .__ dict __。has_key('_ MyClass__initialized')or self .__ dict __。has_key(name):
object .__ setattr __(self,name,value)
else:
raise AttributeError (Attribute%s does not exist。%name)
#使用injector来添加我们的功能
class MyClass(injector,MyClass):
pass
如果你想在c ++中做同样的事情,它有点棘手:
使用命名空间boost :: python;
static void SetAttr(object self,str name,object value)
{
dict d = getattr(self,__dict__);
if(d.has_key(name)){
setattr(self,name,value);
} else {
std :: stringstream ss;
ss<< 方法<<提取< std :: string>(name)<< 不存在。;
PyErr_SetString(PyExc_AttributeError,ss.str()。c_str());
throw error_already_set();
}
}
BOOST_PYTHON_MODULE(mymodule)
{
class_< MyClass>(MyClass)
.def(__ setattr__ ,& SetAttr);
}
I am wrapping (many) c++ classes with boost::python. If I mistype the attribute name when assigning it from a python script, python silently creates new instance attribute, which is never what I indent. Is there a way to catch such events (and raise exception?)?
I've seen a few posts on the web on the topic, but none of them seemd to give a definitive answer; I was trying to override __setattr__
& friends, but I was not able to get it right; plus I was concerned about possible performance impact.
Cheers, Vaclav
__setattr__
is in fact the way to go. If you're worried about performance, I think python still does this call internally anyway, so overriding the function should not cost any more. The problem with __setattr__
is that if you're trying to keep your implementation inside your c++ code, it can be tricky to implement.
Here's a python version:
# MyClassBase defined in C++ library
class MyClass(object):
def __init__(self):
self.test1 = 'test1'
self.__initialized = True
def __setattr__(self, name, value):
if not self.__dict__.has_key('_MyClass__initialized') or self.__dict__.has_key(name):
object.__setattr__(self, name, value)
else:
raise AttributeError("Attribute %s does not exist." % name)
def main():
o = MyClass()
print o.test1
o.test1 = 'test1_set'
print o.test1
# This will throw
o.test2 = 'test2_set'
if __name__ == '__main__':
main()
Here's a way to do it using metaclasses. The advantage of this method is that you only have to define the __setattr__
function once, then you can just define each class using your injector class:
# create an injector metaclass to add functionality to
# our modules, you can reuse BoostPythonMetaclass for
# all boost::python-exported objects
BoostPythonMetaclass = MyClass.__class__
class injector(object):
class __metaclass__(BoostPythonMetaclass):
def __init__(self, name, bases, dict):
for b in bases:
if type(b) not in (self, type):
for k,v in dict.items():
setattr(b,k,v)
setattr(b, '__initialized', True)
return type.__init__(self, name, bases, dict)
def __setattr__(self, name, value):
if not self.__dict__.has_key('_MyClass__initialized') or self.__dict__.has_key(name):
object.__setattr__(self, name, value)
else:
raise AttributeError("Attribute %s does not exist." % name)
# Use the injector to add our functionality
class MyClass(injector, MyClass):
pass
If you want to do the same in c++, it's a bit trickier:
using namespace boost::python;
static void SetAttr(object self, str name, object value)
{
dict d = getattr(self, "__dict__");
if(d.has_key(name)) {
setattr(self, name, value);
} else {
std::stringstream ss;
ss << "Method" << extract<std::string>(name) << "does not exist.";
PyErr_SetString(PyExc_AttributeError, ss.str().c_str());
throw error_already_set();
}
}
BOOST_PYTHON_MODULE(mymodule)
{
class_<MyClass>("MyClass")
.def("__setattr__", &SetAttr);
}
这篇关于从c ++捕获boost :: python-wrapped类的实例属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!