【问题标题】:Creating an inheritable Python type with PyCxx使用 PyCxx 创建可继承的 Python 类型
【发布时间】:2009-02-14 03:44:08
【问题描述】:

我和一个朋友最近一直在玩弄各种 Python C++ 包装器,试图找到一个既能满足一些专业项目又能满足业余爱好的项目的需求。我们都在PyCxx 上进行了磨练,作为轻量级和易于交互之间的良好平衡,同时隐藏了 Python C api 的一些最丑陋的部分。然而,PyCxx 在公开类型方面并不是非常健壮(即:它指示您创建类型工厂而不是实现构造函数),我们一直在努力填补空白,以便以更实用的方式公开我们的类型.为了填补这些空白,我们求助于 C api。

然而,这给我们留下了一些问题,api 文档似乎并没有深入介绍(当涉及到时,答案有时会相互矛盾)。基本的总体问题很简单:必须为 Python 类型定义什么才能用作基本类型?我们发现,要让 PyC​​xx 类作为类型起作用,我们需要显式定义 tp_new 和 tp_dealloc 并将类型设置为模块属性,并且我们需要在 [我们的类型]->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 测试代码如下所示:

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'

这让我很困惑。就好像从它继承完全隐藏了基类!如果有人可以提供一些关于我们所缺少的内容的见解,我们将不胜感激!谢谢!


编辑:好的,在发布这篇文章大约五秒钟后,我想起了我们之前想尝试的一些东西。我将以下代码添加到 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

所以还在寻求帮助!谢谢!

【问题讨论】:

  • 你在研究 pylibcat++ 吗?

标签: python python-c-api pycxx


【解决方案1】:

您必须将kitty 声明为class new_style_class: public Py::PythonClass&lt; new_style_class &gt;。请参阅simple.cxxhttp://cxx.svn.sourceforge.net/viewvc/cxx/trunk/CXX/Demo/Python3/ 上的 Python 测试用例。

Python 2.2 引入了新样式的类,其中允许用户子类化内置类型(如您的新内置类型)。继承在您的示例中不起作用,因为它定义了一个旧式类。

【讨论】:

    【解决方案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++ getattr 方法应该检查 this.ob_type-&gt;tp_dict (如果 this 是子类的一个实例,这当然是子类的字典)并且只有在你失败时才调用 getattr_methods在那里找到name(参见 PyDict_ API 函数)。

    另外,我认为您不应该自己设置 tp_dealloc:我看不出您的实现如何改进 PyCxx 的默认 extension_object_deallocator

    【讨论】:

      猜你喜欢
      • 2015-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-19
      • 2012-04-06
      • 1970-01-01
      • 2021-08-23
      • 2020-12-05
      相关资源
      最近更新 更多