В последнее время мы с другом возились с различными обертками Python C ++, пытаясь найти тот, который отвечает потребностям как профессиональных, так и хобби-проектов. Мы оба оттачивали PyCxx как хороший баланс между легкостью и простотой интерфейса, скрывая при этом некоторые из самых уродливых кусочков API Python C. Однако PyCxx не очень устойчив, когда дело доходит до представления типов (т. Е. Он инструктирует вас создавать фабрики типов, а не реализовывать конструкторы), и мы работали над заполнением пробелов, чтобы представить наши типы более функциональным образом. , Чтобы заполнить эти пробелы, обратимся к C api.
Это оставило нас с некоторыми вопросами, однако, документация 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'
Что выводит меня из себя. Как будто наследование от него полностью скрывает базовый класс! Если бы кто-нибудь мог дать некоторое представление о том, что нам не хватает, это будет оценено! Спасибо!
РЕДАКТИРОВАТЬ: Хорошо, примерно через пять секунд после публикации я вспомнил кое-что, что мы хотели попробовать ранее. Я добавил следующий код для котенка -
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
Так что все еще ищем помощи! Спасибо!