日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

導(dǎo)讀:使用 C 擴(kuò)展為 Python/ target=_blank class=infotextkey>Python 提供特定功能。

本文字?jǐn)?shù):7993,閱讀時(shí)長(zhǎng)大約: 9分鐘

 

使用 C 擴(kuò)展為 Python 提供特定功能。

 

在前一篇文章中,我介紹了 opensource.com。在大多數(shù)系統(tǒng)上,CPython 是默認(rèn)的解釋器,而且根據(jù)民意調(diào)查顯示,它還是最流行的解釋器。Cpython 的獨(dú)有功能是使用擴(kuò)展 API 用 C 語(yǔ)言編寫 Python 模塊。用 C 語(yǔ)言編寫 Python 模塊允許你將計(jì)算密集型代碼轉(zhuǎn)移到 C,同時(shí)保留 Python 的易用性。

在本文中,我將向你展示如何編寫一個(gè) C++ 擴(kuò)展模塊。使用 C++ 而不是 C,因?yàn)榇蠖鄶?shù)編譯器通常都能理解這兩種語(yǔ)言。我必須提前說(shuō)明缺點(diǎn):以這種方式構(gòu)建的 Python 模塊不能移植到其他解釋器中。它們只與 CPython 解釋器配合工作。因此,如果你正在尋找一種可移植性更好的與 C 語(yǔ)言模塊交互的方式,考慮下使用 docs.python.org 模塊。

源代碼

和往常一樣,你可以在 github.com 上找到相關(guān)的源代碼。倉(cāng)庫(kù)中的 C++ 文件有以下用途:

?my_py_module.cpp: Python 模塊MyModule的定義

?my_cpp_class.h: 一個(gè)頭文件 - 只有一個(gè)暴露給 Python 的 C++ 類

?my_class_py_type.h/cpp: Python 形式的 C++ 類

?pydbg.cpp: 用于調(diào)試的單獨(dú)應(yīng)用程序

本文構(gòu)建的 Python 模塊不會(huì)有任何實(shí)際用途,但它是一個(gè)很好的示例。

構(gòu)建模塊

在查看源代碼之前,你可以檢查它是否能在你的系統(tǒng)上編譯。 opensource.com 來(lái)創(chuàng)建構(gòu)建的配置信息,因此你的系統(tǒng)上必須安裝 CMake。為了配置和構(gòu)建這個(gè)模塊,可以讓 Python 去執(zhí)行這個(gè)過(guò)程:

 

  1.  

    $ python3 setup.py build

     

 

或者手動(dòng)執(zhí)行:

 

  1.  

    $ cmake -B build

     

  2.  

    $ cmake --build build

     

 

之后,在/build子目錄下你會(huì)有一個(gè)名為MyModule. so的文件。

定義擴(kuò)展模塊

首先,看一下my_py_module.cpp文件,尤其是PyInit_MyModule函數(shù):

 

  1.  

    PyMODINIT_FUNC

     

  2.  

    PyInit_MyModule(void) {

     

  3.  

    PyObject* module = PyModule_Create(&my_module);

     

  4.  

     

  5.  

    PyObject *myclass = PyType_FromSpec(&spec_myclass);

     

  6.  

    if (myclass == NULL){

     

  7.  

    return NULL;

     

  8.  

    }

     

  9.  

    Py_INCREF(myclass);

     

  10.  

     

  11.  

    if(PyModule_AddObject(module, "MyClass", myclass) < 0){

     

  12.  

    Py_DECREF(myclass);

     

  13.  

    Py_DECREF(module);

     

  14.  

    return NULL;

     

  15.  

    }

     

  16.  

    return module;

     

  17.  

    }

     

 

這是本例中最重要的代碼,因?yàn)樗?CPython 的入口點(diǎn)。一般來(lái)說(shuō),當(dāng)一個(gè) Python C 擴(kuò)展被編譯并作為共享對(duì)象二進(jìn)制文件提供時(shí),CPython 會(huì)在同名二進(jìn)制文件中(.so)搜索PyInit_函數(shù),并在試圖導(dǎo)入時(shí)執(zhí)行它。

無(wú)論是聲明還是實(shí)例,所有 Python 類型都是 docs.python.org 的一個(gè)指針。在此函數(shù)的第一部分中,module通過(guò)PyModule_Create(...)創(chuàng)建的。正如你在module詳述(my_py_module,同名文件)中看到的,它沒有任何特殊的功能。

之后,調(diào)用 docs.python.org 為自定義類型MyClass創(chuàng)建一個(gè) Python docs.python.org 定義。一個(gè)堆類型對(duì)應(yīng)于一個(gè) Python 類,然后將它賦值給MyModule模塊。

注意,如果其中一個(gè)函數(shù)返回失敗,則必須減少以前創(chuàng)建的復(fù)制對(duì)象的引用計(jì)數(shù),以便解釋器刪除它們。

指定 Python 類型

MyClass詳述在 github.com 中可以找到,它作為 docs.python.org 的一個(gè)實(shí)例:

 

  1.  

    static PyType_Spec spec_myclass = {

     

  2.  

    "MyClass", // name

     

  3.  

    sizeof(MyClassObject) + sizeof(MyClass), // basicsize

     

  4.  

    0, // itemsize

     

  5.  

    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // flags

     

  6.  

    MyClass_slots // slots

     

  7.  

    };

     

 

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

 

  1.  

    typedef struct {

     

  2.  

    PyObject_HEAD

     

  3.  

    int m_value;

     

  4.  

    MyClass* m_myclass;

     

  5.  

    } MyClassObject;

     

 

Python 表示的話就是 docs.python.org 類型,由PyObject_HEAD宏和其他一些成員定義。成員m_value視為普通類成員,而成員m_myclass只能在 C++ 代碼內(nèi)部訪問(wèn)。

docs.python.org 定義了一些其他功能:

 

  1.  

    static PyType_Slot MyClass_slots[] = {

     

  2.  

    {Py_tp_new, (void*)MyClass_new},

     

  3.  

    {Py_tp_init, (void*)MyClass_init},

     

  4.  

    {Py_tp_dealloc, (void*)MyClass_Dealloc},

     

  5.  

    {Py_tp_members, MyClass_members},

     

  6.  

    {Py_tp_methods, MyClass_methods},

     

  7.  

    {0, 0} /* Sentinel */

     

  8.  

    };

     

 

在這里,設(shè)置了一些初始化和析構(gòu)函數(shù)的跳轉(zhuǎn),還有普通的類方法和成員,還可以設(shè)置其他功能,如分配初始屬性字典,但這是可選的。這些定義通常以一個(gè)哨兵結(jié)束,包含NULL值。

要完成類型詳述,還包括下面的方法和成員表:

 

  1.  

    static PyMethodDef MyClass_methods[] = {

     

  2.  

    {"addOne", (PyCFunction)MyClass_addOne, METH_NOARGS, PyDoc_STR("Return an incrmented integer")},

     

  3.  

    {NULL, NULL} /* Sentinel */

     

  4.  

    };

     

  5.  

     

  6.  

    static struct PyMemberDef MyClass_members[] = {

     

  7.  

    {"value", T_INT, offsetof(MyClassObject, m_value)},

     

  8.  

    {NULL} /* Sentinel */

     

  9.  

    };

     

 

在方法表中,定義了 Python 方法addOne,它指向相關(guān)的 C++ 函數(shù)MyClass_addOne。它充當(dāng)了一個(gè)包裝器,它在 C++ 類中調(diào)用addOne()方法。

在成員表中,只有一個(gè)為演示目的而定義的成員。不幸的是,在 docs.python.org 中使用的 en.cppreference.com 不允許添加 C++ 類型到MyClassObject。如果你試圖放置一些 C++ 類型的容器(如 en.cppreference.com),編譯器會(huì)抱怨一些內(nèi)存布局相關(guān)的警告。

初始化和析構(gòu)

MyClass_new方法只為MyClassObject提供一些初始值,并為其類型分配內(nèi)存:

 

  1.  

    PyObject *MyClass_new(PyTypeObject *type, PyObject *args, PyObject *kwds){

     

  2.  

    std::cout << "MtClass_new() called!" << std::endl;

     

  3.  

     

  4.  

    MyClassObject *self;

     

  5.  

    self = (MyClassObject*) type->tp_alloc(type, 0);

     

  6.  

    if(self != NULL){ // -> 分配成功

     

  7.  

    // 賦初始值

     

  8.  

    self->m_value = 0;

     

  9.  

    self->m_myclass = NULL;

     

  10.  

    }

     

  11.  

    return (PyObject*) self;

     

  12.  

    }

     

 

實(shí)際的初始化發(fā)生在MyClass_init中,它對(duì)應(yīng)于 Python 中的 docs.python.org 方法:

 

  1.  

    int MyClass_init(PyObject *self, PyObject *args, PyObject *kwds){

     

  2.  

     

  3.  

    ((MyClassObject *)self)->m_value = 123;

     

  4.  

     

  5.  

    MyClassObject* m = (MyClassObject*)self;

     

  6.  

    m->m_myclass = (MyClass*)PyObject_Malloc(sizeof(MyClass));

     

  7.  

     

  8.  

    if(!m->m_myclass){

     

  9.  

    PyErr_SetString(PyExc_RuntimeError, "Memory allocation failed");

     

  10.  

    return -1;

     

  11.  

    }

     

  12.  

     

  13.  

    try {

     

  14.  

    new (m->m_myclass) MyClass();

     

  15.  

    } catch (const std::exception& ex) {

     

  16.  

    PyObject_Free(m->m_myclass);

     

  17.  

    m->m_myclass = NULL;

     

  18.  

    m->m_value = 0;

     

  19.  

    PyErr_SetString(PyExc_RuntimeError, ex.what());

     

  20.  

    return -1;

     

  21.  

    } catch(...) {

     

  22.  

    PyObject_Free(m->m_myclass);

     

  23.  

    m->m_myclass = NULL;

     

  24.  

    m->m_value = 0;

     

  25.  

    PyErr_SetString(PyExc_RuntimeError, "Initialization failed");

     

  26.  

    return -1;

     

  27.  

    }

     

  28.  

     

  29.  

    return 0;

     

  30.  

    }

     

 

如果你想在初始化過(guò)程中傳遞參數(shù),必須在此時(shí)調(diào)用 docs.python.org。簡(jiǎn)單起見,本例將忽略初始化過(guò)程中傳遞的所有參數(shù)。在函數(shù)的第一部分中,PyObject指針(self)被強(qiáng)轉(zhuǎn)為MyClassObject類型的指針,以便訪問(wèn)其他成員。此外,還分配了 C++ 類的內(nèi)存,并執(zhí)行了構(gòu)造函數(shù)。

注意,為了防止內(nèi)存泄漏,必須仔細(xì)執(zhí)行異常處理和內(nèi)存分配(還有釋放)。當(dāng)引用計(jì)數(shù)將為零時(shí),MyClass_dealloc函數(shù)負(fù)責(zé)釋放所有相關(guān)的堆內(nèi)存。在文檔中有一個(gè)章節(jié)專門講述關(guān)于 C 和 C++ 擴(kuò)展的內(nèi)存管理。

包裝方法

從 Python 類中調(diào)用相關(guān)的 C++ 類方法很簡(jiǎn)單:

 

  1.  

    PyObject* MyClass_addOne(PyObject *self, PyObject *args){

     

  2.  

    assert(self);

     

  3.  

     

  4.  

    MyClassObject* _self = reinterpret_cast(self);

     

  5.  

    unsigned long val = _self->m_myclass->addOne();

     

  6.  

    return PyLong_FromUnsignedLong(val);

     

  7.  

    }

     

 

同樣,PyObject參數(shù)(self)被強(qiáng)轉(zhuǎn)為MyClassObject類型以便訪問(wèn)m_myclass,它指向 C++ 對(duì)應(yīng)類實(shí)例的指針。有了這些信息,調(diào)用addOne()類方法,并且結(jié)果以 docs.python.org 返回。

3 種方法調(diào)試

出于調(diào)試目的,在調(diào)試配置中編譯 CPython 解釋器是很有價(jià)值的。詳細(xì)描述參閱 docs.python.org。只要下載了預(yù)安裝的解釋器的其他調(diào)試符號(hào),就可以按照下面的步驟進(jìn)行操作。

GNU 調(diào)試器

當(dāng)然,老式的 opensource.com 也可以派上用場(chǎng)。源碼中包含了一個(gè) github.com 文件,定義了一些選項(xiàng)和斷點(diǎn),另外還有一個(gè) github.com 腳本,它會(huì)創(chuàng)建一個(gè)調(diào)試構(gòu)建并啟動(dòng)一個(gè) GDB 會(huì)話:

Gnu 調(diào)試器(GDB)對(duì)于 Python C 和 C++ 擴(kuò)展非常有用

GDB 使用腳本文件 github.com 調(diào)用 CPython 解釋器,它允許你輕松定義你想要使用 Python 擴(kuò)展模塊執(zhí)行的所有操作。

C++ 應(yīng)用

另一種方法是將 CPython 解釋器嵌入到一個(gè)單獨(dú)的 C++ 應(yīng)用程序中。可以在倉(cāng)庫(kù)的 github.com 文件中找到:

 

  1.  

    int main(int argc, char *argv[], char *envp[])

     

  2.  

    {

     

  3.  

    Py_SetProgramName(L"DbgPythonCppExtension");

     

  4.  

    Py_Initialize();

     

  5.  

     

  6.  

    PyObject *pmodule = PyImport_ImportModule("MyModule");

     

  7.  

    if (!pmodule) {

     

  8.  

    PyErr_Print();

     

  9.  

    std::cerr << "Failed to import module MyModule" << std::endl;

     

  10.  

    return -1;

     

  11.  

    }

     

  12.  

     

  13.  

    PyObject *myClassType = PyObject_GetAttrString(pmodule, "MyClass");

     

  14.  

    if (!myClassType) {

     

  15.  

    std::cerr << "Unable to get type MyClass from MyModule" << std::endl;

     

  16.  

    return -1;

     

  17.  

    }

     

  18.  

     

  19.  

    PyObject *myClassInstance = PyObject_CallObject(myClassType, NULL);

     

  20.  

     

  21.  

    if (!myClassInstance) {

     

  22.  

    std::cerr << "Instantioation of MyClass failed" << std::endl;

     

  23.  

    return -1;

     

  24.  

    }

     

  25.  

     

  26.  

    Py_DecRef(myClassInstance); // invoke deallocation

     

  27.  

    return 0;

     

  28.  

    }

     

 

使用 docs.python.org,可以導(dǎo)入擴(kuò)展模塊并對(duì)其執(zhí)行操作。它允許你在本地 IDE 環(huán)境中進(jìn)行調(diào)試,還能讓你更好地控制傳遞或來(lái)自擴(kuò)展模塊的變量。

缺點(diǎn)是創(chuàng)建一個(gè)額外的應(yīng)用程序的成本很高。

VSCode 和 VSCodium LLDB 擴(kuò)展

使用像 github.com 這樣的調(diào)試器擴(kuò)展可能是最方便的調(diào)試選項(xiàng)。倉(cāng)庫(kù)包含了一些 VSCode/VSCodium 的配置文件,用于構(gòu)建擴(kuò)展,如 github.com、 github.com 和調(diào)用調(diào)試器( github.com)。這種方法結(jié)合了前面幾種方法的優(yōu)點(diǎn):在圖形 IDE 中調(diào)試,在 Python 腳本文件中定義操作,甚至在解釋器提示符中動(dòng)態(tài)定義操作。

VSCodium 有一個(gè)集成的調(diào)試器。

用 C++ 擴(kuò)展 Python

Python 的所有功能也可以從 C 或 C++ 擴(kuò)展中獲得。雖然用 Python 寫代碼通常認(rèn)為是一件容易的事情,但用 C 或 C++ 擴(kuò)展 Python 代碼是一件痛苦的事情。另一方面,雖然原生 Python 代碼比 C++ 慢,但 C 或 C++ 擴(kuò)展可以將計(jì)算密集型任務(wù)提升到原生機(jī)器碼的速度。

你還必須考慮 ABI 的使用。穩(wěn)定的 ABI 提供了一種方法來(lái)保持舊版本 CPython 的向后兼容性,如 docs.python.org 所述。

最后,你必須自己權(quán)衡利弊。如果你決定使用 C 語(yǔ)言來(lái)擴(kuò)展 Python 中的一些功能,你已經(jīng)看到了如何實(shí)現(xiàn)它。

via:

作者: 選題: 譯者: 校對(duì):

本文由 原創(chuàng)編譯, 榮譽(yù)推出

LCTT 譯者 :MjSeven

翻譯: 175.0 篇

貢獻(xiàn): 1798 天

2018-01-30

2023-01-01

https://linux.cn/lctt/MjSeven

歡迎遵照 CC-BY-SA 協(xié)議規(guī)定轉(zhuǎn)載,

如需轉(zhuǎn)載,請(qǐng)?jiān)谖恼孪铝粞?“ 轉(zhuǎn)載:公眾號(hào)名稱”,

我們將為您添加白名單,授權(quán)“ 轉(zhuǎn)載文章時(shí)可以修改”。

分享到:
標(biāo)簽:Python
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定