从c ++捕获boost :: python-wrapped类的实例属性 [英] Catch creation of instance attributes of boost::python-wrapped classes from c++

查看:194
本文介绍了从c ++捕获boost :: python-wrapped类的实例属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆