使用PyCxx创建可继承的Python类型

2024-07-08 11:22:01 发布

您现在位置:Python中文网/ 问答频道 /正文

我和朋友最近一直在忙于各种Python C++包装,试图找到一个既满足专业又有兴趣项目的需求。我们都在PyCxx上进行了深入的研究,在轻量级和易于交互的同时隐藏了pythoncapi中最丑陋的部分。但是,PyCxx在公开类型方面并不是非常健壮(例如,它指示您创建类型工厂而不是实现构造函数),我们一直在努力填补空白,以便以更具功能的方式公开我们的类型。为了填补这些空白,我们求助于C api。在

然而,这给我们留下了一些问题,api文档似乎并没有很深入地涵盖这些问题(如果有,答案有时会相互矛盾)。基本的首要问题很简单:要使Python类型作为基类型运行,必须定义什么?我们发现,为了让PyCxx类作为一个类型来工作,我们需要显式地定义tp_new和tp_dealoc,并将类型设置为module属性,我们需要在[our type]->;tp峎flags上设置Py_TPFLAGS_BASETYPE,但除此之外,我们仍在黑暗中摸索。在

以下是迄今为止我们的代码:

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测试代码如下所示:

^{pr2}$

有趣的是,如果您将所有meanKitty位注释掉,脚本会运行,cat会喵喵叫,但是如果您取消注释meanKitty类,Python会突然给出以下信息:

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

把我搞糊涂了。好像从它继承就完全隐藏了基类!如果有人能提供一些关于我们缺失什么的见解,我们将不胜感激!谢谢!在


编辑:好吧,在发布这篇文章五秒钟后,我想起了我们之前想尝试的一些东西。我给kitty添加了以下代码-

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

所以仍然在寻求帮助!谢谢!在


Tags: py类型newtypevirtualstaticpubliccat
2条回答

我只对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++ ^ {< CD1>}方法应该检查^ {< CD2>}(当然,它将是子类的DICT,如果^ {CD3>}是子类的实例),并且如果在那里找不到^ {CD5>}(请参阅PyDISMIAPI函数),只调用^ {CD4>}。在

另外,我不认为您应该自己设置tp_dealloc:我看不出您的实现在PyCxx的默认extension_object_deallocator上有什么改进。在

必须将kitty声明为class new_style_class: public Py::PythonClass< new_style_class >。请参见simple.cxx和位于{a1}的Python测试用例。在

Python2.2引入了一些新的类,这些类允许用户对内置类型进行子类化(比如新的内置类型)。继承在您的示例中不起作用,因为它定义了一个旧样式的类。在

相关问题 更多 >

    热门问题