Inhaltsverzeichnis
Quellcode
构建模块
定义扩展模块
指定 Python 类型
初始化和析构
包装方法
3 种方法调试
GNU 调试器
C++ 应用
VSCode 和 VSCodium LLDB 扩展
用 C++ 扩展 Python
Heim Backend-Entwicklung Python-Tutorial Schreiben Sie ein C++-Erweiterungsmodul für Python

Schreiben Sie ein C++-Erweiterungsmodul für Python

Apr 12, 2023 pm 01:34 PM
python 模块 c++

Schreiben Sie ein C++-Erweiterungsmodul für Python

Verwenden Sie C-Erweiterungen, um spezifische Funktionen für Python bereitzustellen.

Im vorherigen Artikel habe ich ​​Sechs Python-Interpreter​​ vorgestellt. CPython ist auf den meisten Systemen der Standardinterpreter und laut Umfragen der beliebteste. Einzigartig an Cpython ist die Möglichkeit, Python-Module mithilfe einer Erweiterungs-API in C zu schreiben. Durch das Schreiben von Python-Modulen in C können Sie rechenintensiven Code nach C verschieben und gleichzeitig die Benutzerfreundlichkeit von Python beibehalten.

In diesem Artikel zeige ich Ihnen, wie Sie ein C++-Erweiterungsmodul schreiben. Verwenden Sie C++ anstelle von C, da die meisten Compiler im Allgemeinen beide Sprachen verstehen. Die Nachteile muss ich gleich vorweg erwähnen: Auf diese Weise erstellte Python-Module sind nicht auf andere Interpreter portierbar. Sie funktionieren nur mit dem CPython-Interpreter. Wenn Sie daher nach einer tragbareren Möglichkeit zur Interaktion mit C-Sprachmodulen suchen, sollten Sie die Verwendung des Moduls ​​ctypes​​ in Betracht ziehen.

Quellcode

Den entsprechenden Quellcode finden Sie wie gewohnt auf ​​GitHub​​. Die C++-Dateien im Repository haben die folgenden Verwendungszwecke:

  • ​my_py_module.cpp​​: Python module​​MyModule​ Definition
  • ​my_py_module.cpp​​: Python 模块​​MyModule​​ 的定义
  • ​my_cpp_class.h​​: 一个头文件 - 只有一个暴露给 Python 的 C++ 类
  • ​my_class_py_type.h/cpp​​: Python 形式的 C++ 类
  • ​pydbg.cpp​​: 用于调试的单独应用程序

本文构建的 Python 模块不会有任何实际用途,但它是一个很好的示例。

构建模块

在查看源代码之前,你可以检查它是否能在你的系统上编译。​​我使用 CMake​​ 来创建构建的配置信息,因此你的系统上必须安装 CMake。为了配置和构建这个模块,可以让 Python 去执行这个过程:

$ python3 setup.py build
Nach dem Login kopieren

或者手动执行:

$ cmake -B build$ cmake --build build
Nach dem Login kopieren

之后,在 ​​/build​​ 子目录下你会有一个名为 ​​MyModule. so​​ 的文件。

定义扩展模块

首先,看一下 ​​my_py_module.cpp​​ 文件,尤其是 ​​PyInit_MyModule​​ 函数:

PyMODINIT_FUNCPyInit_MyModule(void) {PyObject* module = PyModule_Create(&my_module);PyObject *myclass = PyType_FromSpec(&spec_myclass);if (myclass == NULL){return NULL;}Py_INCREF(myclass);if(PyModule_AddObject(module, "MyClass", myclass) < 0){Py_DECREF(myclass);Py_DECREF(module);return NULL;}return module;}
Nach dem Login kopieren

这是本例中最重要的代码,因为它是 CPython 的入口点。一般来说,当一个 Python C 扩展被编译并作为共享对象二进制文件提供时,CPython 会在同名二进制文件中(​​<ModuleName>.so​​)搜索 ​​PyInit_<ModuleName>​​my_cpp_class.h​ ​: Eine Header-Datei – nur eine C++-Klasse, die für Python verfügbar gemacht wird

​my_class_py_type.h/cpp​​: C++-Klasse in Python-Form#🎜🎜#​​pydbg.cpp​: Separate Anwendung zum Debuggen </li></ul>#🎜🎜#Das in diesem Artikel erstellte Python-Modul wird keinen praktischen Nutzen haben, ist aber ein gutes Beispiel. #🎜🎜##🎜🎜#Module erstellen#🎜🎜##🎜🎜# Bevor Sie sich den Quellcode ansehen, können Sie prüfen, ob er auf Ihrem System kompiliert wird. ​<a target="_blank" href="https://www.php.cn/link/37ecd27608480aa3569a511a638ca74f">​Ich verwende CMake​#🎜🎜#​, um die Build-Konfigurationsinformationen zu erstellen, daher muss es auf Ihrem installiert werden System CMake. Um dieses Modul zu konfigurieren und zu erstellen, können Sie Python diesen Prozess ausführen lassen: #🎜🎜#<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>static PyType_Spec spec_myclass = {&quot;MyClass&quot;,// namesizeof(MyClassObject) + sizeof(MyClass),// basicsize0,// itemsizePy_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // flagsMyClass_slots // slots};</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div>#🎜🎜# oder manuell: #🎜🎜#<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>typedef struct {PyObject_HEADint m_value;MyClass*m_myclass;} MyClassObject;</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div>#🎜🎜#After ​<code style="background-color: rgb (231, 243, 237); padding: 0px 3px; overflow-wrap: break-indent: 0px;">​/build​​ ist ein Code namens ​ ​MyModule.so​​ Datei. #🎜🎜##🎜🎜#Erweiterungsmodul definieren#🎜🎜##🎜🎜#Schauen Sie sich zuerst an ​​my_py_module.cpp​​ Datei, insbesondere ​​PyInit_MyModule​​ Funktion: #🎜🎜#
static PyType_Slot MyClass_slots[] = {{Py_tp_new, (void*)MyClass_new},{Py_tp_init,(void*)MyClass_init},{Py_tp_dealloc, (void*)MyClass_Dealloc},{Py_tp_members, MyClass_members},{Py_tp_methods, MyClass_methods},{0, 0} /* Sentinel */};
Nach dem Login kopieren
Nach dem Login kopieren
#🎜🎜#Das ist Der wichtigste Code in diesem Beispiel, da er den Einstiegspunkt in CPython darstellt. Wenn eine Python-C-Erweiterung kompiliert und als Shared-Object-Binärdatei bereitgestellt wird, wird CPython im Allgemeinen in die Binärdatei mit demselben Namen aufgenommen (​​<ModuleName>.so​​) Suche ​​PyInit_<ModuleName>​​ Führen Sie es beim Import aus. #🎜🎜#

无论是声明还是实例,所有 Python 类型都是 ​​PyObject​​ 的一个指针。在此函数的第一部分中,​​module​​ 通过 ​​PyModule_Create(...)​​ 创建的。正如你在 ​​module​​ 详述(​​my_py_module​​,同名文件)中看到的,它没有任何特殊的功能。

之后,调用 ​​PyType_FromSpec​​ 为自定义类型 ​​MyClass​​ 创建一个 Python ​​堆类型​​ 定义。一个堆类型对应于一个 Python 类,然后将它赋值给 ​​MyModule​​ 模块。

注意,如果其中一个函数返回失败,则必须减少以前创建的复制对象的引用计数,以便解释器删除它们。

指定 Python 类型

​MyClass​​ 详述在 ​​my_class_py_type.h​​ 中可以找到,它作为 ​​PyType_Spec​​ 的一个实例:

static PyType_Spec spec_myclass = {"MyClass",// namesizeof(MyClassObject) + sizeof(MyClass),// basicsize0,// itemsizePy_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // flagsMyClass_slots // slots};
Nach dem Login kopieren
Nach dem Login kopieren

它定义了一些基本类型信息,它的大小包括 Python 表示的大小(​​MyClassObject​​)和普通 C++ 类的大小(​​MyClass​​)。​​MyClassObject​​ 定义如下:

typedef struct {PyObject_HEADint m_value;MyClass*m_myclass;} MyClassObject;
Nach dem Login kopieren
Nach dem Login kopieren

Python 表示的话就是 ​​PyObject​​ 类型,由 ​​PyObject_HEAD​​ 宏和其他一些成员定义。成员 ​​m_value​​ 视为普通类成员,而成员 ​​m_myclass​​ 只能在 C++ 代码内部访问。

​PyType_Slot​​ 定义了一些其他功能:

static PyType_Slot MyClass_slots[] = {{Py_tp_new, (void*)MyClass_new},{Py_tp_init,(void*)MyClass_init},{Py_tp_dealloc, (void*)MyClass_Dealloc},{Py_tp_members, MyClass_members},{Py_tp_methods, MyClass_methods},{0, 0} /* Sentinel */};
Nach dem Login kopieren
Nach dem Login kopieren

在这里,设置了一些初始化和析构函数的跳转,还有普通的类方法和成员,还可以设置其他功能,如分配初始属性字典,但这是可选的。这些定义通常以一个哨兵结束,包含 ​​NULL​​ 值。

要完成类型详述,还包括下面的方法和成员表:

static PyMethodDef MyClass_methods[] = {{"addOne", (PyCFunction)MyClass_addOne, METH_NOARGS,PyDoc_STR("Return an incrmented integer")},{NULL, NULL} /* Sentinel */};static struct PyMemberDef MyClass_members[] = {{"value", T_INT, offsetof(MyClassObject, m_value)},{NULL} /* Sentinel */};
Nach dem Login kopieren

在方法表中,定义了 Python 方法 ​​addOne​​,它指向相关的 C++ 函数 ​​MyClass_addOne​​。它充当了一个包装器,它在 C++ 类中调用 ​​addOne()​​ 方法。

在成员表中,只有一个为演示目的而定义的成员。不幸的是,在 ​​PyMemberDef​​ 中使用的 ​​offsetof​​ 不允许添加 C++ 类型到 ​​MyClassObject​​。如果你试图放置一些 C++ 类型的容器(如 ​​std::optional​​),编译器会抱怨一些内存布局相关的警告。

初始化和析构

​MyClass_new​​ 方法只为 ​​MyClassObject​​ 提供一些初始值,并为其类型分配内存:

PyObject *MyClass_new(PyTypeObject *type, PyObject *args, PyObject *kwds){std::cout << "MtClass_new() called!" << std::endl;MyClassObject *self;self = (MyClassObject*) type->tp_alloc(type, 0);if(self != NULL){ // -> 分配成功// 赋初始值self->m_value = 0;self->m_myclass = NULL; }return (PyObject*) self;}
Nach dem Login kopieren

实际的初始化发生在 ​​MyClass_init​​ 中,它对应于 Python 中的 ​​__init__()​​ 方法:

int MyClass_init(PyObject *self, PyObject *args, PyObject *kwds){((MyClassObject *)self)->m_value = 123;MyClassObject* m = (MyClassObject*)self;m->m_myclass = (MyClass*)PyObject_Malloc(sizeof(MyClass));if(!m->m_myclass){PyErr_SetString(PyExc_RuntimeError, "Memory allocation failed");return -1;}try {new (m->m_myclass) MyClass();} catch (const std::exception& ex) {PyObject_Free(m->m_myclass);m->m_myclass = NULL;m->m_value = 0;PyErr_SetString(PyExc_RuntimeError, ex.what());return -1;} catch(...) {PyObject_Free(m->m_myclass);m->m_myclass = NULL;m->m_value = 0;PyErr_SetString(PyExc_RuntimeError, "Initialization failed");return -1;}return 0;}
Nach dem Login kopieren

如果你想在初始化过程中传递参数,必须在此时调用 ​​PyArg_ParseTuple​​。简单起见,本例将忽略初始化过程中传递的所有参数。在函数的第一部分中,​​PyObject​​ 指针(​​self​​)被强转为 ​​MyClassObject​​ 类型的指针,以便访问其他成员。此外,还分配了 C++ 类的内存,并执行了构造函数。

注意,为了防止内存泄漏,必须仔细执行异常处理和内存分配(还有释放)。当引用计数将为零时,​​MyClass_dealloc​​ 函数负责释放所有相关的堆内存。在文档中有一个章节专门讲述关于 C 和 C++ 扩展的内存管理。

包装方法

从 Python 类中调用相关的 C++ 类方法很简单:

PyObject* MyClass_addOne(PyObject *self, PyObject *args){assert(self);MyClassObject* _self = reinterpret_cast<MyClassObject*>(self);unsigned long val = _self->m_myclass->addOne();return PyLong_FromUnsignedLong(val);}
Nach dem Login kopieren

同样,​​PyObject​​ 参数(​​self​​)被强转为 ​​MyClassObject​​ 类型以便访问 ​​m_myclass​​,它指向 C++ 对应类实例的指针。有了这些信息,调用 ​​addOne()​​ 类方法,并且结果以 ​​Python 整数对象​​ 返回。

3 种方法调试

出于调试目的,在调试配置中编译 CPython 解释器是很有价值的。详细描述参阅 ​​官方文档​​。只要下载了预安装的解释器的其他调试符号,就可以按照下面的步骤进行操作。

GNU 调试器

当然,老式的 ​​GNU 调试器(GDB)​​ 也可以派上用场。源码中包含了一个 ​​gdbinit​​ 文件,定义了一些选项和断点,另外还有一个 ​​gdb.sh​​ 脚本,它会创建一个调试构建并启动一个 GDB 会话:

Gnu 调试器(GDB)对于 Python C 和 C++ 扩展非常有用

Gnu 调试器(GDB)对于 Python C 和 C++ 扩展非常有用

GDB 使用脚本文件 ​​main.py​​ 调用 CPython 解释器,它允许你轻松定义你想要使用 Python 扩展模块执行的所有操作。

C++ 应用

另一种方法是将 CPython 解释器嵌入到一个单独的 C++ 应用程序中。可以在仓库的 ​​pydbg.cpp​​ 文件中找到:

int main(int argc, char *argv[], char *envp[]){Py_SetProgramName(L"DbgPythonCppExtension");Py_Initialize();PyObject *pmodule = PyImport_ImportModule("MyModule");if (!pmodule) {PyErr_Print();std::cerr << "Failed to import module MyModule" << std::endl;return -1;}PyObject *myClassType = PyObject_GetAttrString(pmodule, "MyClass");if (!myClassType) {std::cerr << "Unable to get type MyClass from MyModule" << std::endl;return -1;}PyObject *myClassInstance = PyObject_CallObject(myClassType, NULL);if (!myClassInstance) {std::cerr << "Instantioation of MyClass failed" << std::endl;return -1;}Py_DecRef(myClassInstance); // invoke deallocationreturn 0;}
Nach dem Login kopieren

使用 ​​高级接口​​,可以导入扩展模块并对其执行操作。它允许你在本地 IDE 环境中进行调试,还能让你更好地控制传递或来自扩展模块的变量。

缺点是创建一个额外的应用程序的成本很高。

VSCode 和 VSCodium LLDB 扩展

使用像 ​​CodeLLDB​​ 这样的调试器扩展可能是最方便的调试选项。仓库包含了一些 VSCode/VSCodium 的配置文件,用于构建扩展,如 ​​task.json​​、​​CMake Tools​​ 和调用调试器(​​launch.json​​)。这种方法结合了前面几种方法的优点:在图形 IDE 中调试,在 Python 脚本文件中定义操作,甚至在解释器提示符中动态定义操作。

VSCodium 有一个集成的调试器。

VSCodium 有一个集成的调试器。

用 C++ 扩展 Python

Python 的所有功能也可以从 C 或 C++ 扩展中获得。虽然用 Python 写代码通常认为是一件容易的事情,但用 C 或 C++ 扩展 Python 代码是一件痛苦的事情。另一方面,虽然原生 Python 代码比 C++ 慢,但 C 或 C++ 扩展可以将计算密集型任务提升到原生机器码的速度。

你还必须考虑 ABI 的使用。稳定的 ABI 提供了一种方法来保持旧版本 CPython 的向后兼容性,如 ​​文档​​ 所述。

最后,你必须自己权衡利弊。如果你决定使用 C 语言来扩展 Python 中的一些功能,你已经看到了如何实现它。

Das obige ist der detaillierte Inhalt vonSchreiben Sie ein C++-Erweiterungsmodul für Python. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

<🎜>: Bubble Gum Simulator Infinity - So erhalten und verwenden Sie Royal Keys
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Nordhold: Fusionssystem, erklärt
1 Monate vor By 尊渡假赌尊渡假赌尊渡假赌
Mandragora: Flüstern des Hexenbaum
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
<🎜> obscur: Expedition 33 - So erhalten Sie perfekte Chroma -Katalysatoren
2 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

Java-Tutorial
1677
14
PHP-Tutorial
1278
29
C#-Tutorial
1257
24
Golang und C: Parallelität gegen Rohgeschwindigkeit Golang und C: Parallelität gegen Rohgeschwindigkeit Apr 21, 2025 am 12:16 AM

Golang ist in Gleichzeitigkeit besser als C, während C bei Rohgeschwindigkeit besser als Golang ist. 1) Golang erreicht durch Goroutine und Kanal eine effiziente Parallelität, die zum Umgang mit einer großen Anzahl von gleichzeitigen Aufgaben geeignet ist. 2) C über Compiler -Optimierung und Standardbibliothek bietet es eine hohe Leistung in der Nähe der Hardware, die für Anwendungen geeignet ist, die eine extreme Optimierung erfordern.

Golang gegen C: Leistung und Geschwindigkeitsvergleich Golang gegen C: Leistung und Geschwindigkeitsvergleich Apr 21, 2025 am 12:13 AM

Golang ist für schnelle Entwicklung und gleichzeitige Szenarien geeignet, und C ist für Szenarien geeignet, in denen extreme Leistung und Kontrolle auf niedriger Ebene erforderlich sind. 1) Golang verbessert die Leistung durch Müllsammlung und Parallelitätsmechanismen und eignet sich für die Entwicklung von Webdiensten mit hoher Konsequenz. 2) C erreicht die endgültige Leistung durch das manuelle Speicherverwaltung und die Compiler -Optimierung und eignet sich für eingebettete Systementwicklung.

Python vs. JavaScript: Entwicklungsumgebungen und Tools Python vs. JavaScript: Entwicklungsumgebungen und Tools Apr 26, 2025 am 12:09 AM

Sowohl Python als auch JavaScripts Entscheidungen in Entwicklungsumgebungen sind wichtig. 1) Die Entwicklungsumgebung von Python umfasst Pycharm, Jupyternotebook und Anaconda, die für Datenwissenschaft und schnelles Prototyping geeignet sind. 2) Die Entwicklungsumgebung von JavaScript umfasst Node.JS, VSCODE und WebPack, die für die Entwicklung von Front-End- und Back-End-Entwicklung geeignet sind. Durch die Auswahl der richtigen Tools nach den Projektbedürfnissen kann die Entwicklung der Entwicklung und die Erfolgsquote der Projekte verbessert werden.

Python vs. C: Verständnis der wichtigsten Unterschiede Python vs. C: Verständnis der wichtigsten Unterschiede Apr 21, 2025 am 12:18 AM

Python und C haben jeweils ihre eigenen Vorteile, und die Wahl sollte auf Projektanforderungen beruhen. 1) Python ist aufgrund seiner prägnanten Syntax und der dynamischen Typisierung für die schnelle Entwicklung und Datenverarbeitung geeignet. 2) C ist aufgrund seiner statischen Tipp- und manuellen Speicherverwaltung für hohe Leistung und Systemprogrammierung geeignet.

Laravel vs. Python (mit Frameworks): Eine vergleichende Analyse Laravel vs. Python (mit Frameworks): Eine vergleichende Analyse Apr 21, 2025 am 12:15 AM

Laravel ist für Projekte geeignet, dass Teams mit PHP vertraut sind und umfangreiche Funktionen erfordern, während Python -Frameworks von den Projektanforderungen abhängen. 1. Laravel bietet elegante Syntax und reichhaltige Funktionen, die für Projekte geeignet sind, die eine schnelle Entwicklung und Flexibilität erfordern. 2. Django ist aufgrund des Konzepts "Batterieeinschluss" für komplexe Anwendungen geeignet. 3.Flask eignet sich für schnelle Prototypen und kleine Projekte und bietet eine große Flexibilität.

Golang gegen Python: Die Vor- und Nachteile Golang gegen Python: Die Vor- und Nachteile Apr 21, 2025 am 12:17 AM

GolangissidealforbuildingsCalablesSystemduetoitseffizienz und Konsumverkehr, whilepythonexcelsinquickScriptingandDataanalyseduetoitssimplication und VacevastEcosystem.golangsDesineScouragesCouragescournations, tadelcodedeanDitsGoroutaTinoutgoroutaTinoutgoroutaTinoutsGoroutinesGoroutinesGoroutsGoroutins, t

Was ist eine statische Analyse in C? Was ist eine statische Analyse in C? Apr 28, 2025 pm 09:09 PM

Die Anwendung der statischen Analyse in C umfasst hauptsächlich das Erkennen von Problemen mit Speicherverwaltung, das Überprüfen von Code -Logikfehlern und die Verbesserung der Codesicherheit. 1) Statische Analyse kann Probleme wie Speicherlecks, Doppelfreisetzungen und nicht initialisierte Zeiger identifizieren. 2) Es kann ungenutzte Variablen, tote Code und logische Widersprüche erkennen. 3) Statische Analysetools wie die Deckung können Pufferüberlauf, Ganzzahlüberlauf und unsichere API -Aufrufe zur Verbesserung der Codesicherheit erkennen.

C und XML: Erforschen der Beziehung und Unterstützung C und XML: Erforschen der Beziehung und Unterstützung Apr 21, 2025 am 12:02 AM

C interagiert mit XML über Bibliotheken von Drittanbietern (wie Tinyxml, Pugixml, Xerces-C). 1) Verwenden Sie die Bibliothek, um XML-Dateien zu analysieren und in C-verarbeitbare Datenstrukturen umzuwandeln. 2) Konvertieren Sie beim Generieren von XML die C -Datenstruktur in das XML -Format. 3) In praktischen Anwendungen wird XML häufig für Konfigurationsdateien und Datenaustausch verwendet, um die Entwicklungseffizienz zu verbessern.

See all articles