python - 具有 Python C 扩展的类(不是方法)的完整和最小示例?

标签 python python-3.x class cpython python-c-api

在任何地方,我都可以很容易地找到一个使用 Python C Extensions 编写方法的示例。并在 Python 中使用它。喜欢这个:Python 3 extension example

$ python3
>>> import hello
>>> hello.hello_world()
Hello, world!
>>> hello.hello('world')
Hello, world!

如何编写一个 hello word 功能齐全的 Python 类(不仅仅是一个模块方法)?

我认为这是 How to wrap a C++ object using pure Python Extension API (python3)?问题有一个例子,但它似乎并不是最小的,因为他正在使用(或包装?)C++ 类。

例如:

class ClassName(object):
    """docstring for ClassName"""

    def __init__(self, hello):
        super().__init__()
        self.hello = hello

    def talk(self, world):
        print( '%s %s' % ( self.hello, world ) )

这个带有 C 扩展的 Python 类示例的等价物是什么?

我会这样使用它:
from .mycextensionsmodule import ClassName

classname = ClassName("Hello")
classname.talk( 'world!' )
# prints "Hello world!"

我的目标是编写一个完全用 C 语言编写的类以提高性能(我项目中的所有其他类都将使用 Python,除了这个)。我不是在寻找使用 ctypes 的可移植性, 两个黑框都没有使用 Boost.PythonSWIG .只是一个纯粹用 Python C 扩展编写的高性能类。

在我得到这个 Hello word 之后工作时,我可以在 Python Extensive 文档中找到自己的答案:
  • https://docs.python.org/3/c-api/
  • https://docs.python.org/3/extending/extending.html
  • 最佳答案

    另见:Python instance method in C
    创建名为 MANIFEST.in 的文件

    include README.md
    include LICENSE.txt
    
    recursive-include source *.h
    
    创建名为 setup.py 的文件
    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    from setuptools import setup, Extension
    
    __version__ = '0.1.0'
    
    setup(
            name = 'custom',
            version = __version__,
    
            package_data = {
                    '': [ '**.txt', '**.md', '**.py', '**.h', '**.hpp', '**.c', '**.cpp' ],
                },
    
            ext_modules = [
                Extension(
                    name = 'custom',
                    sources = [
                        'source/custom.cpp',
                    ],
                    include_dirs = ['source'],
                )
            ],
        )
    
    创建名为 source/custom.cpp 的文件
    #define PY_SSIZE_T_CLEAN
    #include <Python.h>
    #include "structmember.h"
    
    typedef struct {
        PyObject_HEAD
        PyObject *first; /* first name */
        PyObject *last;  /* last name */
        int number;
    } CustomObject;
    
    static int
    Custom_traverse(CustomObject *self, visitproc visit, void *arg)
    {
        Py_VISIT(self->first);
        Py_VISIT(self->last);
        return 0;
    }
    
    static int
    Custom_clear(CustomObject *self)
    {
        Py_CLEAR(self->first);
        Py_CLEAR(self->last);
        return 0;
    }
    
    static void
    Custom_dealloc(CustomObject *self)
    {
        PyObject_GC_UnTrack(self);
        Custom_clear(self);
        Py_TYPE(self)->tp_free((PyObject *) self);
    }
    
    static PyObject *
    Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
    {
        CustomObject *self;
        self = (CustomObject *) type->tp_alloc(type, 0);
        if (self != NULL) {
            self->first = PyUnicode_FromString("");
            if (self->first == NULL) {
                Py_DECREF(self);
                return NULL;
            }
            self->last = PyUnicode_FromString("");
            if (self->last == NULL) {
                Py_DECREF(self);
                return NULL;
            }
            self->number = 0;
        }
        return (PyObject *) self;
    }
    
    static int
    Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
    {
        static char *kwlist[] = {"first", "last", "number", NULL};
        PyObject *first = NULL, *last = NULL, *tmp;
    
        if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
                                         &first, &last,
                                         &self->number))
            return -1;
    
        if (first) {
            tmp = self->first;
            Py_INCREF(first);
            self->first = first;
            Py_DECREF(tmp);
        }
        if (last) {
            tmp = self->last;
            Py_INCREF(last);
            self->last = last;
            Py_DECREF(tmp);
        }
        return 0;
    }
    
    static PyMemberDef Custom_members[] = {
        {"number", T_INT, offsetof(CustomObject, number), 0,
         "custom number"},
        {NULL}  /* Sentinel */
    };
    
    static PyObject *
    Custom_getfirst(CustomObject *self, void *closure)
    {
        Py_INCREF(self->first);
        return self->first;
    }
    
    static int
    Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
    {
        if (value == NULL) {
            PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
            return -1;
        }
        if (!PyUnicode_Check(value)) {
            PyErr_SetString(PyExc_TypeError,
                            "The first attribute value must be a string");
            return -1;
        }
        Py_INCREF(value);
        Py_CLEAR(self->first);
        self->first = value;
        return 0;
    }
    
    static PyObject *
    Custom_getlast(CustomObject *self, void *closure)
    {
        Py_INCREF(self->last);
        return self->last;
    }
    
    static int
    Custom_setlast(CustomObject *self, PyObject *value, void *closure)
    {
        if (value == NULL) {
            PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
            return -1;
        }
        if (!PyUnicode_Check(value)) {
            PyErr_SetString(PyExc_TypeError,
                            "The last attribute value must be a string");
            return -1;
        }
        Py_INCREF(value);
        Py_CLEAR(self->last);
        self->last = value;
        return 0;
    }
    
    static PyGetSetDef Custom_getsetters[] = {
        {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
         "first name", NULL},
        {"last", (getter) Custom_getlast, (setter) Custom_setlast,
         "last name", NULL},
        {NULL}  /* Sentinel */
    };
    
    static PyObject *
    Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
    {
        return PyUnicode_FromFormat("%S %S", self->first, self->last);
    }
    
    static PyMethodDef Custom_methods[] = {
        {"name", (PyCFunction) Custom_name, METH_NOARGS,
         "Return the name, combining the first and last name"
        },
        {NULL}  /* Sentinel */
    };
    
    static PyTypeObject CustomType = {
        PyVarObject_HEAD_INIT(NULL, 0)
        .tp_name = "custom.Custom",
        .tp_doc = "Custom objects",
        .tp_basicsize = sizeof(CustomObject),
        .tp_itemsize = 0,
        .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
        .tp_new = Custom_new,
        .tp_init = (initproc) Custom_init,
        .tp_dealloc = (destructor) Custom_dealloc,
        .tp_traverse = (traverseproc) Custom_traverse,
        .tp_clear = (inquiry) Custom_clear,
        .tp_members = Custom_members,
        .tp_methods = Custom_methods,
        .tp_getset = Custom_getsetters,
    };
    
    static PyModuleDef custommodule = {
        PyModuleDef_HEAD_INIT,
        .m_name = "custom",
        .m_doc = "Example module that creates an extension type.",
        .m_size = -1,
    };
    
    PyMODINIT_FUNC
    PyInit_custom(void)
    {
        PyObject *m;
        if (PyType_Ready(&CustomType) < 0)
            return NULL;
    
        m = PyModule_Create(&custommodule);
        if (m == NULL)
            return NULL;
    
        Py_INCREF(&CustomType);
        PyModule_AddObject(m, "Custom", (PyObject *) &CustomType);
        return m;
    }
    
    然后,要编译并安装它,您可以运行:
    pip3 install . -v
    python3 setup.py install
    
    作为这个问题的旁注How to use setuptools packages and ext_modules with the same name?不要混在同一个项目上*.py文件和 Python C 扩展,即仅使用纯 C/C++,构建 Python C 扩展而不添加 packages = [ 'package_name' ]条目,因为它们导致 Python C 扩展代码运行 30%,即如果程序需要 7 秒才能运行,现在使用 *.py文件,这将需要 11 秒。
    引用:
  • https://docs.python.org/3/extending/newtypes_tutorial.html#supporting-cyclic-garbage-collection
  • 关于python - 具有 Python C 扩展的类(不是方法)的完整和最小示例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56203127/

    相关文章:

    objective-c - 结构或类

    Python multiprocessing.Pool 不会立即启动

    python - 追加后如何打印列表中的所有元素?

    python-3.x - 在pytorch中重置神经网络的参数

    python - Pandas :用数据框添加星期日期

    Java:更改方法参数的最有效方法

    C#:与 Object 一起使用的类

    python - 为什么 Flask 不应该与内置服务器一起部署?

    python - 导入错误 : cannot import name UserSerializer while creating a demo rest api

    python - 是否存在用于滚动日志/配置文件的 python 工具?