2009-02-14 2 views
3

В последнее время мы с другом встречались с различными упаковщиками Python C++, пытаясь найти тот, который отвечает потребностям как профессиональных, так и хобби. Мы оба оттачивали на PyCxx как хороший баланс между легким и удобным интерфейсом, спрятав некоторые из самых уродливых бит Python C api. Однако PyCxx не очень надежна, когда речь заходит об экспонировании типов (т. Е.: Он инструктирует вас создавать фабрики типов, а не создавать конструкторы), и мы работаем над заполнением пробелов, чтобы выявить наши типы более функциональным образом , Чтобы заполнить эти пробелы, перейдем к C api.Создание наследуемого типа Python с помощью PyCxx

Это оставляет нам некоторые вопросы, однако, что документация api, похоже, не покрывает большую глубину (и когда это происходит, ответы иногда противоречивы). Основной всеобъемлющий вопрос заключается в следующем: что должно быть определено для типа Python для использования в качестве базового типа? Мы обнаружили, что для класса PyCxx, который будет функционировать как тип, мы должны явно определить tp_new и tp_dealloc и установить тип как атрибут модуля, и что нам нужно установить Py_TPFLAGS_BASETYPE на [наш тип] -> tp_flags, но дальше что мы все еще нащупываем темноту.

Вот наш код до сих пор:

class kitty : public Py::PythonExtension<kitty> { 
public: 
    kitty() : Py::PythonExtension<kitty>() {} 
    virtual ~kitty() {} 

    static void init_type() { 
     behaviors().name("kitty"); 
     add_varargs_method("speak", &kitty::speak); 
    } 

    static PyObject* tp_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { 
     return static_cast<PyObject*>(new kitty()); 
    } 

    static void tp_dealloc(PyObject *obj) { 
     kitty* k = static_cast<kitty*>(obj); 
     delete k; 
    } 

private: 
    Py::Object speak(const Py::Tuple &args) { 
     cout << "Meow!" << endl; 
     return Py::None(); 
    } 
}; 

// cat Module 
class cat_module : public Py::ExtensionModule<cat_module> { 
public: 
    cat_module() : Py::ExtensionModule<cat_module>("cat") { 

     kitty::init_type(); 

     // Set up additional properties on the kitty type object 
     PyTypeObject* kittyType = kitty::type_object(); 
     kittyType->tp_new = &kitty::tp_new; 
     kittyType->tp_dealloc = &kitty::tp_dealloc; 
     kittyType->tp_flags |= Py_TPFLAGS_BASETYPE; 

     // Expose the kitty type through the module 
     module().setAttr("kitty", Py::Object((PyObject*)kittyType)); 

     initialize(); 
    } 
    virtual ~cat_module() {} 
}; 

extern "C" void initcat() { 
    static cat_module* cat = new cat_module(); 
} 

И наш тестовый код Python выглядит следующим образом:

import cat 

class meanKitty(cat.kitty): 
    def scratch(self): 
     print "hiss! *scratch*" 

myKitty = cat.kitty() 
myKitty.speak() 

meanKitty = meanKitty() 
meanKitty.speak() 
meanKitty.scratch() 

Любопытно бит, что если вы прокомментировать все meanKitty биты из, сценарий бежит, а кошка мяукает просто отлично, но если вы раскомментируете класс MeanKitty внезапно, Python дает нам это:

AttributeError: 'kitty' object has no attribute 'speak' 

Что сбивает меня с ума. Это как если бы наследование от него полностью скрывает базовый класс! Если бы кто-нибудь мог дать некоторое представление о том, чего нам не хватает, это было бы оценено! Благодаря!

EDIT: Хорошо, примерно через пять секунд после публикации этого сообщения я вспомнил то, что мы хотели попробовать раньше. Я добавил следующий код в копилке -

virtual Py::Object getattr(const char *name) { 
    return getattr_methods(name); 
} 

И теперь мы мяуканье на обоих кошечек в Python! все еще не полностью там, однако, потому что теперь я получаю это:

Traceback (most recent call last): 
    File "d:\Development\Junk Projects\PythonCxx\Toji.py", line 12, in <module> 
    meanKitty.scratch() 
AttributeError: scratch 

Так что ищите помощь! Благодаря!

+1

Вы работаете на pylibcat ++? – joeforker

ответ

3

Вы должны объявить kitty как class new_style_class: public Py::PythonClass<new_style_class>.См. simple.cxx и тестовый пример Python по адресу http://cxx.svn.sourceforge.net/viewvc/cxx/trunk/CXX/Demo/Python3/.

Python 2.2 представил классы нового стиля, которые, среди прочего, позволяют пользователю подклассировать встроенные типы (например, ваш новый встроенный тип). Наследование не работало в вашем примере, потому что оно определяет класс старого стиля.

1

Я только сделал крошечную работу с PyCxx, и я не компилятор, но я подозреваю, что вы видите, это похоже на следующую ситуацию, выраженные в чистом Python:

>>> class C(object): 
... def __getattribute__(self, key): 
...  print 'C', key 
... 
>>> class D(C): 
... def __init__(self): 
...  self.foo = 1 
... 
>>> D().foo 
C foo 
>>> 

Мое предположение, что метод C++ getattr должен проверить this.ob_type->tp_dict (который, конечно, будет подкласс ДИКТ, если this экземпляром подкласса) и вызывать только getattr_methods, если вы не нашли name в там (см PyDict_ API).

Кроме того, я не думаю, что вы должны установить tp_dealloc самостоятельно: я не вижу, как ваша реализация улучшается по умолчанию PyCxx extension_object_deallocator.

Смежные вопросы