Поймать создание атрибутов экземпляров классов boost :: python в c ++ - PullRequest
2 голосов
/ 25 мая 2011

Я упаковываю (многие) классы c ++ с boost :: python.Если я неправильно наберу имя атрибута при назначении его из скрипта python, python молча создает новый атрибут экземпляра, который никогда не будет тем, что я делаю отступом.Есть ли способ отловить такие события (и вызвать исключение?)?

Я видел несколько постов в сети на эту тему, но ни одно из них, похоже, не дало однозначного ответа;Я пытался переопределить __setattr__ & друзей, но я не смог сделать это правильно;плюс меня беспокоило возможное влияние на производительность.

Ура, Вацлав

Ответы [ 2 ]

3 голосов
/ 25 мая 2011

__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);
}
2 голосов
/ 02 марта 2018
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

}
...