从 c++ 捕获 boost::python 包装类的实例属性的创建

发布于 2024-11-09 17:26:03 字数 225 浏览 2 评论 0原文

我用 boost::python 包装(许多)c++ 类。如果我在从 python 脚本分配属性名称时输错了属性名称,Python 会默默地创建新的实例属性,而这绝不是我缩进的内容。有没有办法捕获此类事件(并引发异常?)?

我在网上看到了一些关于这个主题的帖子,但似乎没有一个给出明确的答案;我试图覆盖 __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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

静若繁花 2024-11-16 17:26:03

__setattr__ 实际上是正确的方法。如果您担心性能,我认为 python 仍然会在内部进行此调用,因此覆盖该函数不应再花费更多成本。 __setattr__ 的问题是,如果您试图将实现保留在 C++ 代码中,那么实现起来可能会很棘手。

这是一个 python 版本:

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

这是一种使用元类来完成此操作的方法。这种方法的优点是你只需要定义一次 __setattr__ 函数,然后你就可以使用你的注入器类来定义每个类:

# 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

如果你想在 C++ 中做同样的事情,那就有点棘手了:

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

__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);
}
成熟的代价 2024-11-16 17:26:03
using namespace boost::python;

static object __setattr__native;           //#Object for saving native boost __setattr__() method
//#Hook-setter for method __setattr__(). Checks attribute exists and call native __setattr__ if its exists
static void SetAttr_only_exists(object self, str name, object value)
{
    getattr(self, name);                   //#Get attribute or exception if attribute not exists
    __setattr__native(self, name, value);  //#Call native __setattr__()
    return;
}
BOOST_PYTHON_MODULE(mymodule)
{
    myClass = class_<MyClass>("MyClass")
       .def("someMethod", &MyClass::someMethod);

    __setattr__native = myClass.attr("__setattr__");   //#Saving boost __setattr__() method
    myClass.def("__setattr__", &SetAttr_only_exists); //#Set __setattr__() hook method

}
using namespace boost::python;

static object __setattr__native;           //#Object for saving native boost __setattr__() method
//#Hook-setter for method __setattr__(). Checks attribute exists and call native __setattr__ if its exists
static void SetAttr_only_exists(object self, str name, object value)
{
    getattr(self, name);                   //#Get attribute or exception if attribute not exists
    __setattr__native(self, name, value);  //#Call native __setattr__()
    return;
}
BOOST_PYTHON_MODULE(mymodule)
{
    myClass = class_<MyClass>("MyClass")
       .def("someMethod", &MyClass::someMethod);

    __setattr__native = myClass.attr("__setattr__");   //#Saving boost __setattr__() method
    myClass.def("__setattr__", &SetAttr_only_exists); //#Set __setattr__() hook method

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文