共用方式為


在 Visual Studio 中建立適用於 Python 的C++延伸模組

在本文中,您會為 CPython 建置C++擴充模組,以計算雙曲正切值,並從 Python 程式代碼呼叫它。 例程會先在 Python 中實作,以示範在 C++ 中實作相同例程的相對效能提升。

以 C++ (或 C) 撰寫的程式代碼模組通常用來擴充 Python 解釋器的功能。 擴充模組有三種主要類型:

  • 加速器模組:啟用加速效能。 因為 Python 是解譯的語言,因此您可以在 C++ 中撰寫加速器模組,以提升效能。
  • 包裝函式模組:向 Python 程式代碼公開現有的 C/C++介面,或公開更容易從 Python 使用的類似 Python 的 API。
  • 低階系統存取模組:建立系統存取模組以達到運行時間、作業系統或基礎硬體的較低層級功能CPython

本文示範兩種方式可讓 python 使用C++擴充模組:

  • 使用標準 CPython 延伸模組,如 Python 檔中所述。
  • 使用 PyBind11,我們建議使用 C++11,因為它的簡單性。 若要確保相容性,請確定您使用的是其中一個較新版本的 Python。

本逐步解說的完整範例可在 GitHub 上取得,位於 python-samples-vs-cpp-extension

先決條件

  • 已安裝 Python 開發工作負載的 Visual Studio 2017 或更新版本。 工作負載包含 Python 原生開發工具,可新增原生延伸模組所需的C++工作負載和工具組。

    Python 開發選項清單的螢幕快照,其中醒目提示 [Python 原生開發工具] 選項。

    如需安裝選項的詳細資訊,請參閱 安裝適用於Visual Studio的 Python 支援

    備註

    當您安裝 數據科學和分析應用程式 工作負載時,預設會安裝 Python 和 Python 原生開發工具 選項。

  • 如果您個別安裝 Python,請務必在 Python 安裝程式的 [進階選項] 底下選取 [下載偵錯符號]。 您必須使用此選項,才能在 Python 程式代碼與機器碼之間使用混合模式偵錯。

建立 Python 應用程式

請遵循下列步驟來建立 Python 應用程式。

  1. 選取 [檔案>>專案],在 Visual Studio 中建立新的 Python專案

  2. 在 [ 建立新專案 ] 對話框中,搜尋 python。 選取 [Python 應用程式 ] 範本,然後選取 [ 下一步]。

  3. 輸入項目名稱和位置,然後選取 [建立]。

    Visual Studio 會建立新的專案。 專案會在 方案 總管中開啟,而項目檔 (.py) 會在程式碼編輯器中開啟。

  4. .py 檔案中,貼上下列程序代碼。 若要體驗某些 Python 編輯功能,請嘗試手動輸入程式代碼。

    此程式碼會計算雙曲正切值,而不使用數學庫,您稍後會使用 Python 原生擴展模組來加速。

    小提示

    在C++中重寫程序代碼之前,請先以純 Python 撰寫程式代碼。 如此一來,您就可以更輕鬆地檢查,以確保原生 Python 程式代碼正確無誤。

    from random import random
    from time import perf_counter
    
    # Change the value of COUNT according to the speed of your computer.
    # The value should enable the benchmark to complete in approximately 2 seconds.
    COUNT = 500000
    DATA = [(random() - 0.5) * 3 for _ in range(COUNT)]
    
    e = 2.7182818284590452353602874713527
    
    def sinh(x):
        return (1 - (e ** (-2 * x))) / (2 * (e ** -x))
    
    def cosh(x):
        return (1 + (e ** (-2 * x))) / (2 * (e ** -x))
    
    def tanh(x):
        tanh_x = sinh(x) / cosh(x)
        return tanh_x
    
    def test(fn, name):
        start = perf_counter()
        result = fn(DATA)
        duration = perf_counter() - start
        print('{} took {:.3f} seconds\n\n'.format(name, duration))
    
        for d in result:
            assert -1 <= d <= 1, " incorrect values"
    
    if __name__ == "__main__":
        print('Running benchmarks with COUNT = {}'.format(COUNT))
    
        test(lambda d: [tanh(x) for x in d], '[tanh(x) for x in d] (Python implementation)')
    
  5. 執行程式,請選取 偵錯>不進行偵錯啟動 或按下鍵盤快捷鍵 Ctrl+F5

    命令視窗隨即開啟以顯示程序輸出。

  6. 在輸出中,請注意基準檢驗程序所報告的時間長度。

    在本逐步解說中,基準測試約需 2 秒。

  7. 視需要調整程式代碼中的變數值 COUNT ,讓基準檢驗在您的計算機上大約 2 秒內完成。

  8. 再次執行程式,並確認修改 COUNT 的值會在大約 2 秒內產生基準檢驗。

小提示

當您執行基準測試時,請一律使用 [偵錯>不偵錯啟動] 選項。 此方法有助於避免在 Visual Studio 調試程式內執行程式代碼時所產生的額外負荷。

建立核心C++專案

請遵循下列步驟來建立兩個相同的C++專案 ,superfastcodesuperfastcode2。 稍後,您會在每個專案中使用不同的方法,將C++程式代碼公開給 Python。

  1. [方案總管] 中,以滑鼠右鍵按兩下方案名稱,然後選取 [ 新增>專案]。

    Visual Studio 解決方案可以同時包含 Python 和 C++ 專案,這是使用 Visual Studio 進行 Python 開發的優點之一。

  2. 在 [新增專案] 對話框中,將 [語言] 篩選器設定為 [C++],然後在 [搜尋] 方塊中輸入空白

  3. 在專案範本結果清單中,選取 [空白專案],然後選取 [ 下一步]。

  4. 在 [ 設定新專案 ] 對話框中,輸入 [項目名稱]:

    • 針對第一個專案,輸入 名稱superfastcode
    • 針對第二個專案,輸入 名稱superfastcode2
  5. 選取 ,創建

請務必重複這些步驟並建立兩個專案。

小提示

當您在 Visual Studio 中安裝 Python 原生開發工具時,可以使用替代方法。 您可以從 Python 延伸模組 範本開始,此範本會預先完成本文所述的許多步驟。

如需本文中的逐步解說,從空白專案開始,有助於示範如何逐步建置擴充模組。 瞭解程序之後,您可以使用替代範本在撰寫自己的擴充功能時節省時間。

將C++檔案新增至專案

接下來,將C++檔案新增至每個專案。

  1. [方案總管] 中,展開專案,以滑鼠右鍵按兩下 [來源檔案 ] 節點,然後選取 [ 新增>專案]。

  2. 在檔案範本清單中,選取 [檔案] C++ [.cpp]。

  3. 輸入檔案的 [ 名稱 ] 作為 module.cpp,然後選取 [ 新增]。

    這很重要

    請確定檔名包含 .cpp 擴展名。 Visual Studio 會尋找擴展名 為 .cpp 的檔案,以啟用C++專案屬性頁的顯示。

  4. 在工具列上,展開 [ 組態 ] 下拉功能表,然後選取您的目標組態類型:

    此螢幕快照顯示如何在Visual Studio中設定C++專案的目標組態類型。

    • 若為64位元的 Python 運行環境,請啟動 x64 組態。
    • 針對 32 位元 Python 執行環境,請啟動 Win32 設定。

請務必針對這兩個專案重複這些步驟。

設定項目屬性

將程式代碼新增至新的C++檔案之前,請設定每個C++模組專案的屬性,並測試組態以確保一切正常運作。

您必須設定每個模組之 偵錯發行 組建組態的項目屬性。

  1. [方案總管] 中,以滑鼠右鍵按兩下C++模組專案 (superfastcodesuperfastcode2),然後選取 [ 屬性]。

  2. 設定模組的偵錯版本屬性,然後為發行版本設定相同的屬性:

    在 [ 項目屬性頁] 對話框頂端,設定下列檔案組態選項:

    此螢幕快照顯示如何在 Visual Studio 中設定 C++ 檔案的專案建置和平台選項。

    1. 針對 [ 組態],選取 [ 偵錯 ] 或 [ 發行]。 (您可能會看到這些選項前有啟用標示。)

    2. 針對 平臺,根據您在上一個步驟中的選取專案,選取 作用中 (x64)作用中 (Win32)

      備註

      當您建立自己的專案時,您會想要根據您的特定案例需求,個別設定 偵錯發行 組態。 在此練習中,您會將設定調整為使用 CPython 的發行版本。 此設定會停用 C++ 執行階段的一些偵錯功能,包括斷言。 使用 CPython 偵錯二進位檔 (python_d.exe) 需要不同的設定。

    3. 如下表所述設定其他項目屬性。

      若要變更屬性值,請在屬性欄位中輸入值。 對於某些欄位,您可以選取目前的值來展開選項的下拉功能表,或開啟對話框以協助定義值。

      更新索引標籤上的值之後,請在切換至不同的索引標籤之前選取 [ 套用 ]。此動作有助於確保您的變更維持不變。

      標籤頁和區段 房產 價值觀
      組態屬性>常規 目標名稱 在語句中 from...import 指定要從 Python 參考的模組名稱,例如 superfastcode。 當您定義適用於 Python 的模組時,會在C++程式代碼中使用這個相同的名稱。 若要使用專案的名稱作為模組名稱,請將 預設值保留為 $<ProjectName>。 針對 python_d.exe,請將 新增 _d 至名稱的結尾。
      組態類型 動態連結庫 (.dll)
      組態屬性>高深 目標擴展名 .pyd (Python 擴充模組)
      C/C++>常規 其他包含目錄 視您的安裝情況新增 Python include 資料夾(例如 c:\Python36\include)。
      C/C++>預處理 預處理器定義 如果存在,請將 _DEBUG 值變更為 NDEBUG ,以符合 CPython 的非除錯版本。 當您使用 python_d.exe時,請將此值維持不變。
      C/C++>產生程序代碼 執行時期程式庫 多執行緒 DLL(/MD) 以匹配 CPython 的(非偵錯)版本。 當您使用 python_d.exe時,請將此值保留為 多線程偵錯 DLL (/MDd)
      基本運行時間檢查 預設值
      連結器>一般 其他連結庫目錄 視您的安裝情況新增包含 .lib 檔案的 Python libs 資料夾(例如 c:\Python36\libs)。 請務必指向包含 .lib 檔案的 libs 資料夾,而不是包含.py檔案的 Lib 資料夾。

      這很重要

      如果 C/C++ 索引標籤未顯示為專案屬性的選項,則專案不會包含 Visual Studio 識別為 C/C++原始程式檔的程式代碼檔案。 如果您建立不含 .c擴展名.cpp 的原始程序檔,就可能發生此情況。

      如果您不小心在建立C++檔案時輸入 module.coo ,而不是 module.cpp,Visual Studio 會建立檔案,但不會將文件類型設定為 C/C+ 編譯程式。 需要此檔類型,才能在專案屬性對話框中啟用 C/C++ 屬性索引標籤的存在。 即使您將程式碼檔案重新命名為 .cpp 擴展名,錯誤識別仍會存在。

      若要正確設定程式碼檔類型,請在 [方案總管] 中,以滑鼠右鍵按單擊程式代碼檔案,然後選取 [ 屬性]。 針對 [專案類型],選取 [C/C++ 編譯程式]。

    4. 更新所有屬性之後,請選取 [確定]。

    重複其他組建組態的步驟。

  3. 測試您目前的組態。 針對這兩個 C++ 專案的 除錯正式 組建都重複以下步驟。

    1. 在 Visual Studio 工具列上,將 [建置 組態] 設定為 [ 偵錯 ] 或 [ 發行]:

      顯示如何在Visual Studio中設定C++專案的建置組態的螢幕快照。

    2. 方案瀏覽器 中,以滑鼠右鍵按一下 C++ 專案,然後選取 建置

      .pyd 檔案位於方案資料夾的 [錯] 和 [發行] 底下,而不是在C++項目資料夾本身。

新增程式代碼和測試組態

現在您已準備好將程式代碼新增至C++檔案,並測試 發行 組建。

  1. 針對 superfastcode C++ 專案,在程式代碼編輯器中開啟 module.cpp 檔案。

  2. module.cpp 檔案中,貼上下列程序代碼:

    #include <Windows.h>
    #include <cmath>
    
    const double e = 2.7182818284590452353602874713527;
    
    double sinh_impl(double x) {
        return (1 - pow(e, (-2 * x))) / (2 * pow(e, -x));
    }
    
    double cosh_impl(double x) {
        return (1 + pow(e, (-2 * x))) / (2 * pow(e, -x));
    }
    
    double tanh_impl(double x) {
        return sinh_impl(x) / cosh_impl(x);
    }
    
  3. 儲存您的變更。

  4. 建置C++專案的 發行 組態,以確認您的程式代碼正確無誤。

重複步驟,將程式代碼新增至 superfastcode2 專案的 C++ 檔案,並測試 發行 組建。

將C++項目轉換為 Python 延伸模組

若要讓C++ DLL 成為 Python 的延伸模組,請先修改匯出的方法以與 Python 類型互動。 然後,新增函式以匯出模組,以及模組方法的定義。

下列各節示範如何使用 CPython 延伸模組和 PyBind11 建立擴充功能。 superfasctcode 專案使用 CPython 延伸模組,而 superfasctcode2 專案會實作 PyBind11。

使用 CPython 延伸模組

如需本節所呈現程式碼的詳細資訊,請參閱 Python/C API 參考手冊,特別是 模組對象 頁面。 當您檢閱參考內容時,請務必在右上方的下拉式清單中選取 Python 版本。

  1. 針對 superfastcode C++ 專案,在程式代碼編輯器中開啟 module.cpp 檔案。

  2. module.cpp 檔案頂端新增 語句,以包含 Python.h 頭檔:

    #include <Python.h>
    
  3. 取代方法程式tanh_impl代碼以接受並傳回 Python 類型(也就是 :PyObject*

    PyObject* tanh_impl(PyObject* /* unused module reference */, PyObject* o) {
        double x = PyFloat_AsDouble(o);
        double tanh_x = sinh_impl(x) / cosh_impl(x);
        return PyFloat_FromDouble(tanh_x);
    }
    
  4. 在檔案結尾新增 結構,以定義如何將C++ tanh_impl 函式呈現至 Python:

    static PyMethodDef superfastcode_methods[] = {
        // The first property is the name exposed to Python, fast_tanh
        // The second is the C++ function with the implementation
        // METH_O means it takes a single PyObject argument
        { "fast_tanh", (PyCFunction)tanh_impl, METH_O, nullptr },
    
        // Terminate the array with an object containing nulls
        { nullptr, nullptr, 0, nullptr }
    };
    
  5. 新增另一個結構來定義如何在 Python 程式代碼中參考模組,特別是當您使用 from...import 語句時。

    在此程式代碼中匯入的名稱應該符合 [ 組態屬性>一般>目標名稱] 下項目屬性中的值。

    在下列範例中, "superfastcode" 名稱表示您可以在 Python 中使用 from superfastcode import fast_tanh 語句,因為 fast_tanh 是在 中 superfastcode_methods定義。 在 C++ 專案中,像是 module.cpp 這樣的內部檔名並不重要。

    static PyModuleDef superfastcode_module = {
        PyModuleDef_HEAD_INIT,
        "superfastcode",                        // Module name to use with Python import statements
        "Provides some functions, but faster",  // Module description
        0,
        superfastcode_methods                   // Structure that defines the methods of the module
    };
    
  6. 新增 Python 載入模組時所呼叫的方法。 方法名稱必須是 PyInit_<module-name>,其中 <module-name> 完全符合C++專案的 [ 組態屬性>一般>目標名稱 ] 屬性。 也就是說,方法名稱符合專案所建置 之 .pyd 檔案的檔名。

    PyMODINIT_FUNC PyInit_superfastcode() {
        return PyModule_Create(&superfastcode_module);
    }
    
  7. 建置C++項目並驗證您的程序代碼。 如果您遇到錯誤,請參閱 針對編譯錯誤進行疑難解答一 節。

使用 PyBind11

如果您完成上一節中 superfastcode 專案的步驟,您可能會注意到練習需要樣板程式碼來建立 C++ CPython 延伸模組的模組架構。 在此練習中,您會發現 PyBind11 可簡化程式代碼撰寫程式。 您可以使用C++頭檔中的巨集來完成相同的結果,但程式代碼少得多。 不過,需要額外的步驟,以確保 Visual Studio 可以找到 PyBind11 連結庫並包含檔案。 如需本節中程式代碼的詳細資訊,請參閱 PyBind11 基本概念

安裝 PyBind11

第一個步驟是在專案組態中安裝 PyBind11。 在此練習中,您會使用 [開發人員 PowerShell ] 視窗。

  1. 開啟 [工具>命令行>開發人員 PowerShell ] 視窗。

  2. 開發人員 PowerShell 視窗中,使用 pip 命令 pip install pybind11py -m pip install pybind11安裝 PyBind11。

    Visual Studio 會安裝 PyBind11 及其相依套件。

將 PyBind11 路徑新增至專案

在 PyBind11 安裝之後,您必須將 PyBind11 路徑新增至專案的 [其他 Include Directory ] 屬性。

  1. 開發人員 PowerShell 中,執行 命令 python -m pybind11 --includespy -m pybind11 --includes

    此動作會列印您需要新增至專案屬性的 PyBind11 路徑清單。

  2. 反白顯示視窗中的路徑清單,然後選取視窗工具列上的 [ 複製 (雙頁]。

    顯示如何在 Visual Studio 中醒目提示和複製 [開發人員 PowerShell] 視窗路徑列表的螢幕快照。

    串連路徑清單會新增至剪貼簿。

  3. [方案總管] 中,以滑鼠右鍵按兩下 superfastcode2 專案,然後選取 [ 屬性]。

  4. 在 [屬性頁] 對話框頂部的 [組態] 欄位中,選取 [發行]。 (您可能會看到以 Active 為前綴的此選項。)

  5. 在對話框的 [C/C++>General] 索引標籤中,展開 [其他 Include 目錄] 屬性的下拉功能表,然後選取 [編輯]

  6. 在快顯對話框中,新增複製的路徑清單:

    針對從 開發人員 PowerShell 視窗複製之串連清單中的每個路徑重複這些步驟:

    1. 在快顯對話框工具列上選取新增行(帶有加號的資料夾)

      顯示如何將 PyBind11 路徑新增至 [其他 Include Directory] 屬性的螢幕快照。

      Visual Studio 會在路徑清單頂端新增空行,並將插入游標放在開頭。

    2. 請將 PyBind11 路徑貼入空行中。

      您也可以選取[更多選項](...),並使用彈出式檔案瀏覽器對話框來瀏覽至路徑位置。

      這很重要

      • 如果路徑包含 -I 前置詞,請從路徑中移除前置詞。
      • 若要讓Visual Studio辨識路徑,路徑必須位於個別行上。

      新增路徑之後,Visual Studio 會在 [ 評估值 ] 字段中顯示確認的路徑。

  7. 選取 [確定 ] 以結束快顯對話方塊。

  8. 屬性頁對話框頂端,將滑鼠停留在其他 Include 目錄屬性的值上,並確認 PyBind11 路徑存在。

  9. 選取 [確定 ] 以套用屬性變更。

更新 module.cpp 檔案

最後一個步驟是將 PyBind11 頭檔與巨集程式代碼新增至專案C++檔案。

  1. 針對 superfastcode2 C++ 專案,在程式代碼編輯器中開啟 module.cpp 檔案。

  2. module.cpp 檔案頂端新增 語句,以包含 pybind11.h 頭檔:

    #include <pybind11/pybind11.h>
    
  3. module.cpp 檔案的結尾,新增 PYBIND11_MODULE 巨集,以定義 C++ 函式的進入點:

    namespace py = pybind11;
    
    PYBIND11_MODULE(superfastcode2, m) {
        m.def("fast_tanh2", &tanh_impl, R"pbdoc(
            Compute a hyperbolic tangent of a single argument expressed in radians.
        )pbdoc");
    
    #ifdef VERSION_INFO
        m.attr("__version__") = VERSION_INFO;
    #else
        m.attr("__version__") = "dev";
    #endif
    }
    
  4. 建置C++項目並驗證您的程序代碼。 如果您遇到錯誤,請參閱下一節針對 編譯錯誤進行疑難解答

編譯錯誤疑難排解

如需可能導致C++模組組建失敗的可能問題,請檢閱下列各節。

錯誤:無法找到標頭檔案

Visual Studio 會傳回 錯誤訊息,例如 E1696:無法開啟原始碼檔案 “Python.h”C1083:無法開啟 include 檔案:“Python.h”:沒有這類檔案或目錄

此錯誤表示編譯程式找不到專案所需的標頭 (.h) 檔案。

  • 針對 superfastcode 專案,請確認 C/C++>General>Additional Include Directory 專案屬性包含 Python 安裝 include 資料夾的路徑。 檢閱設定 項目屬性中的步驟。

  • 針對 superfastcode2 專案,請確認相同的專案屬性包含 PyBind11 安裝 include 資料夾的路徑。 檢閱將 PyBind 路徑添加至專案的步驟。

如需存取 Python 安裝組態資訊的詳細資訊,請參閱 Python 檔

錯誤:找不到 Python 連結庫

Visual Studio 會傳回錯誤,指出編譯程式找不到專案所需的連結庫 (DLL) 檔案。

  • 針對 C++ 專案 (superfastcodesuperfastcode2),請確認 連結器>[一般>其他連結庫目錄 ] 屬性包含 Python 安裝 之 libs 資料夾的路徑。 檢閱設定 項目屬性中的步驟。

如需存取 Python 安裝組態資訊的詳細資訊,請參閱 Python 檔

Visual Studio 會報告與專案目標架構設定相關的連結器錯誤,例如 x64 或 Win32。

  • 針對C++專案 (superfastcodesuperfastcode2),請變更目標組態以符合您的 Python 安裝。 例如,如果您的C++專案目標組態為 Win32,但 Python 安裝為 64 位,請將C++專案目標組態變更為 x64。

測試程式代碼並比較結果

既然您已將 DLL 結構化為 Python 延伸模組,您可以從 Python 專案參考它們、匯入模組,以及使用其方法。

讓您的 DLL 可供 Python 使用

您可以透過數種方式將 DLL 提供給 Python 使用。 以下是兩個選項需要考慮:

如果您的 Python 專案和C++項目位於相同的解決方案中,您可以使用下列方法:

  1. [方案總管] 中,以滑鼠右鍵按兩下 Python 專案中的 [ 參考 ] 節點,然後選取 [ 新增參考]。

    請務必針對 Python 專案執行此動作,而不是針對您的 C++ 專案執行此動作。

  2. 在 [ 新增參考] 對話框中,展開 [ 專案] 索引標籤

  3. 選取 superfastcodesuperfastcode2 專案的複選框,然後選取 [ 確定]。

    顯示如何在 Visual Studio 中新增超級快速程式碼項目的參考的螢幕快照。

替代方法是在 Python 環境中安裝C++擴充模組。 這個方法可讓模組可供其他 Python 專案使用。 如需詳細資訊,請參閱 setuptools 項目檔

完成下列步驟,以在 Python 環境中安裝C++擴充模組:

  1. [方案總管] 中,以滑鼠右鍵按兩下您的C++項目,然後選取[ 新增>專案]。

  2. 在檔案範本清單中,選取 [檔案] C++ [.cpp]。

  3. 輸入檔案的 [ 名稱 ] 作為 setup.py,然後選取 [ 新增]。

    請務必使用 Python (.py) 擴展名輸入檔名。 儘管使用 C++ 檔案範本,Visual Studio 仍會將檔案辨識為 Python 程式代碼。

    Visual Studio 會在程式碼編輯器中開啟新的檔案。

  4. 將下列程式代碼貼到新檔案中。 選擇對應至擴充方法的程式代碼版本:

    • CPython 延伸模組superfastcode 專案):

      from setuptools import setup, Extension
      
      sfc_module = Extension('superfastcode', sources = ['module.cpp'])
      
      setup(
          name='superfastcode',
          version='1.0',
          description='Python Package with superfastcode C++ extension',
          ext_modules=[sfc_module]
      )
      
    • PyBind11superfastcode2 專案):

      from setuptools import setup, Extension
      import pybind11
      
      cpp_args = ['-std=c++11', '-stdlib=libc++', '-mmacosx-version-min=10.7']
      
      sfc_module = Extension(
          'superfastcode2',
          sources=['module.cpp'],
          include_dirs=[pybind11.get_include()],
          language='c++',
          extra_compile_args=cpp_args,
      )
      
      setup(
          name='superfastcode2',
          version='1.0',
          description='Python package with superfastcode2 C++ extension (PyBind11)',
          ext_modules=[sfc_module],
      )
      
  5. 在C++專案中,建立名為 pyproject.toml 的第二個檔案,並貼上下列程序代碼:

    [build-system]
    requires = ["setuptools", "wheel", "pybind11"]
    build-backend = "setuptools.build_meta"
    

    TOML.toml) 檔案會針對組態檔使用 Tom 的明顯、最小語言格式。

  6. 若要建置延伸模組,請在程式代碼視窗索引標籤中以滑鼠右鍵按兩下 pyproject.toml 檔名,然後選取 [ 複製完整路徑]。

    顯示如何在 Visual Studio 中將 py 專案完整路徑複製到 toml 檔案的螢幕快照。

    您必須先從路徑中刪除 pyproject.toml 名稱,再使用它。

  7. 方案總管 中,展開方案的 Python 環境 節點。

  8. 以滑鼠右鍵按兩下作用中的 Python 環境(以粗體顯示),然後選取 [ 管理 Python 套件]。

    Python 環境 窗格隨即開啟。

    如果已安裝必要的套件,您會看到它列在此窗格中。

    • 繼續之前,請選取套件名稱旁邊的 X 來卸載它。

    顯示如何在 [Python 環境] 窗格中卸載套件的螢幕快照。

  9. 在 [ Python 環境] 窗格的搜尋方塊中,貼上複製的路徑,並從路徑結尾刪除 pyproject.toml 檔名。

    顯示如何在 [Python 環境] 窗格中輸入路徑以安裝延伸模組的螢幕快照。

  10. 選取 Enter 以從複製路徑的位置安裝模組。

    小提示

    如果安裝因為許可權錯誤而失敗,請將 自變數新增 --user 至命令結尾,然後再試一次安裝。

從 Python 呼叫 DLL

將 DLL 提供給 Python 之後,如上一節所述,您已準備好從 Python 呼叫 superfastcode.fast_tanhsuperfastcode2.fast_tanh2 函式。 然後,您可以將函式效能與 Python 實作進行比較。

請遵循下列步驟,從 Python 呼叫擴充模組 DLL:

  1. 在程式代碼編輯器中開啟 Python 專案的 .py 檔案。

  2. 在檔案結尾,新增下列程式代碼以呼叫從 DLL 匯出的方法,並顯示其輸出:

    from superfastcode import fast_tanh
    test(lambda d: [fast_tanh(x) for x in d], '[fast_tanh(x) for x in d] (CPython C++ extension)')
    
    from superfastcode2 import fast_tanh2
    test(lambda d: [fast_tanh2(x) for x in d], '[fast_tanh2(x) for x in d] (PyBind11 C++ extension)')
    
  3. 透過選擇 偵錯>啟動但不偵錯 或使用鍵盤快捷鍵 Ctrl+F5 來執行 Python 程式。

    備註

    如果 [啟動但不偵錯] 命令無法使用,請在 方案總管 中,以滑鼠右鍵按一下 Python 專案,然後選取 [設定為啟始專案]。

    當程序執行時,請注意,C++例程的執行速度大約比 Python 實作快 5 到 20 倍。

    以下是一般程式輸出的範例:

    Running benchmarks with COUNT = 500000
    [tanh(x) for x in d] (Python implementation) took 0.758 seconds
    
    [fast_tanh(x) for x in d] (CPython C++ extension) took 0.076 seconds
    
    [fast_tanh2(x) for x in d] (PyBind11 C++ extension) took 0.204 seconds
    
  4. 請嘗試增加變數, COUNT 讓時間差異更為明顯。

    C++模組的偵錯版也會比發行版執行得慢,因為偵錯版的優化程度較低,而且包含各種錯誤檢查。 請嘗試在組建組態之間切換以進行比較,但請記得更新您稍早針對發行組態所設定的屬性。

解決進程速度和額外負荷

在輸出中,您可能會注意到 PyBind11 擴充模組不如 CPython 擴充模組快,儘管它應該比純 Python 實作更快。 差異的主要原因是因為使用 METH_O 旗標。 此旗標不支援多個參數、參數名稱或關鍵詞自變數。 PyBind11 會產生稍微更複雜的程式代碼,以提供更類似 Python 的介面給呼叫端。 由於測試程式代碼會呼叫函式 500,000 次,因此結果可以大幅放大額外負荷。

您可以將迴圈移至 for 原生 Python 程式代碼,以進一步降低額外負荷。 此方法牽涉到使用反覆運算器通訊協定(或函式py::iterable PyBind11 類型)來處理每個元素。 拿掉 Python 與 C++ 之間的重複轉換,是減少處理序列所需時間的有效方式。

匯入錯誤疑難解答

如果您在嘗試匯入模組時收到 ImportError 訊息,您可以使用下列其中一種方式加以解析:

  • 當您透過項目參考建置時,請確定您的C++項目屬性符合針對 Python 專案啟用的 Python 環境。 確認 Include.h) 和 Library (DLL) 檔案使用相同的資料夾位置。

  • 請確定您的輸出檔案已正確命名,例如 superfastcode.pyd。 不正確的名稱或擴展名會防止匯入必要的檔案。

  • 如果您使用 setup.py 檔案來安裝模組,請務必在針對 Python 專案啟用的 Python 環境中執行 pip 命令。 當您在 [方案總管] 中展開專案中的作用中 Python 環境時,應該會看到 C++ 專案,例如 superfastcode

偵錯C++程序代碼

Visual Studio 支援一起偵錯 Python 和C++程序代碼。 下列步驟示範 superfastcode C++ 專案的偵錯程式,但 superfastcode2 專案的處理程式相同。

  1. [方案總管] 中,以滑鼠右鍵按兩下 Python 專案,然後選取 [ 屬性]。

  2. 在 [ 屬性] 窗格中,選取 [ 偵錯] 索引卷標,然後 選取 [>偵錯啟用原生程序代碼偵錯 ] 選項。

    小提示

    當您啟用機器碼偵錯時,Python 輸出視窗可能會在程式完成之後立即關閉,而不會暫停並顯示 [按任何按鍵繼續 提示]。 若要在啟用原生程式碼偵錯之後強制暫停並提示,請將參數新增-i至 [執行>解釋器參數] 字段上的 [偵錯] 索引標籤。這個參數會在程式碼執行之後,讓 Python 解釋器進入互動式模式。 程式會等候您選取 ctrl+Z+Enter 來關閉視窗。 替代方法是在 Python 程式結尾新增 import osos.system("pause") 語句。 此程式代碼會複製原始暫停提示。

  3. 選取 [檔案>儲存 ] (或 Ctrl+S) 以儲存屬性變更。

  4. 在 Visual Studio 工具列上,將 [建置 組態] 設定為 [ 偵錯]。

  5. 因為程式碼通常在除錯器中執行的時間較長,因此您可能需要在 Python 專案的 COUNT 檔案中,將變數 的值調整為比預設值小約五倍。 例如,將它從 500000 變更為 100000

  6. 在您的C++程序代碼中,於 方法的第一行 tanh_impl 設定斷點。

  7. 選取> [啟動偵錯] 來啟動偵錯工具,或使用鍵盤快捷方式 F5

    呼叫斷點程式代碼時,調試程式會停止。 如果斷點未觸發,請檢查以確保組態已設定為 Debug,而且您已經儲存專案,因為在您啟動偵錯程式時,專案不會自動儲存。

    此螢幕快照顯示Visual Studio中的C++代碼,其中包含一個斷點。

  8. 在斷點上,您可以依序執行 C++ 程式碼、檢查變數等等。 如需這些功能的詳細資訊,請參閱 一起偵錯 Python 和C++

替代方法

您可以透過各種方式建立 Python 延伸模組,如下表所述。 本文將討論前兩個資料列 CPythonPyBind11

方法 年份 代表使用者
適用於CPython的 C/C++ 擴充模組 1991 標準程式庫
PyBind11 (建議用於C++) 2015
Cython (建議用於 C) 2007 geventkivy
HPy 2019
mypyc 2017
ctypes 2003 oscrypto
cffi 2013 密碼編譯pypy
SWIG 1996 crfsuite
Boost.Python 2002
cppyy 2017