【问题标题】:Defining an inner class using the Python C-API使用 Python C-API 定义内部类
【发布时间】:2016-03-12 05:06:11
【问题描述】:

在 Python 中,定义内部类很简单:

class MyClass(object):

    class MyInnerClass(object):
        pass

... 可以按预期访问内部类,例如通过MyClass.MyInnerClass.

我正在尝试使用扩展模块设置类似的东西。通常,将定义的扩展类型添加到模块的 <modulename>init() 函数中的扩展模块对象中,代码如下:

/// …
if (PyType_Ready(&BufferModel_Type) < 0)      { return; }

/// Add the BufferModel type object to the module
Py_INCREF(&BufferModel_Type);
PyModule_AddObject(module,
    "Buffer",
    (PyObject*)&BufferModel_Type);

/// …

为了设置内部类,我改变了这种方法,尝试添加一个PyTypeObject* 作为另一个PyTypeObject* 的属性,如下所示:

/// …
if (PyType_Ready(&ImageBufferModel_Type) < 0) { return; }
if (PyType_Ready(&ImageModel_Type) < 0)       { return; }

/// Add the ImageBufferModel type object to im.Image
Py_INCREF(&ImageBufferModel_Type);
PyObject_SetAttrString((PyObject*)&ImageModel_Type,
    "ImageBuffer",
    (PyObject*)&ImageBufferModel_Type);
PyType_Modified((PyTypeObject*)&ImageModel_Type);

/// Add the ImageModel type object to the module
Py_INCREF(&ImageModel_Type);
PyModule_AddObject(module,
    "Image",
    (PyObject*)&ImageModel_Type);

/// …

…我认为PyObject_SetAttrString() 会像introduction to “Type Objects” in the C-API docs 那样工作:

类型对象可以使用任何PyObject_*()PyType_*() 函数 […]

...我添加了基于its description in the docs 的调用PyType_Modified()。但是这样:当我编译所有内容并尝试加载扩展时,我得到了这个错误:

>>> import im
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    import im
  File "im/__init__.py", line 2, in <module>
    from im import (
TypeError: can't set attributes of built-in/extension type 'im.Image'

……我想我的做法是错误的;我应该尝试什么?

【问题讨论】:

    标签: python python-c-api setattribute setattr pyobject


    【解决方案1】:

    为此,您需要直接使用tp_dict

    这个字段通常应该在PyType_Ready被调用之前初始化为NULL;它也可以初始化为包含该类型初始属性的字典。一旦PyType_Ready() 初始化了类型,只有当它们不对应于重载操作(如__add__())时,才能将类型的额外属性添加到此字典中。

    你可以这样做,而不是使用PyObject_SetAttrString()

    PyDict_SetItemString(ImageModel_Type.tp_dict, "ImageBuffer", (PyObject*) &ImageModel_Type);
    

    但在这种情况下,文档中的警告适用:

    在字典 C-API 上使用 PyDict_SetItem() 或以其他方式修改 tp_dict 是不安全的。

    所以也许在ImageModel_Type 上调用PyType_Ready 之前初始化tp_dict

    /// Initialize tp_dict with empty dictionary
    ImageModel_Type.tp_dict = PyDict_New();
    if (!ImageModel_Type.tp_dict) { return; }
    
    /// Add the ImageBufferModel type object to im.Image
    if (PyType_Ready(&ImageBufferModel_Type) < 0) { return; }
    Py_INCREF(&ImageBufferModel_Type);
    PyDict_SetItemString(ImageModel_Type.tp_dict,
        "ImageBuffer",
        (PyObject*)&ImageBufferModel_Type);
    
    /// Add the ImageModel type object to the module
    if (PyType_Ready(&ImageModel_Type) < 0) { return; }
    Py_INCREF(&ImageModel_Type);
    PyModule_AddObject(module,
        "Image",
        (PyObject*)&ImageModel_Type);
    

    【讨论】:

    • 哇,谢谢!类型对象文档有时令人困惑,我很欣赏这个直接的答案,这完全有效。
    • 我还应该补充一点,对 PyTypeObject 的 tp_dict 数据成员进行更改可用于在 Python 端实现元类通常实现的许多相同目的(例如,合成基于类的描述符 á la Django 的 ORM 等等)。
    猜你喜欢
    • 2016-04-21
    • 2017-11-19
    • 1970-01-01
    • 1970-01-01
    • 2021-02-14
    • 2014-11-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多