指南:使用ctypes優化Python代碼

2021-12-28 Python程式設計師

注:本文中的代碼是在GNU AGPLv3下授權的。

當我沒有找到一個整體式的使用ctypes的指南時,我寫了這個指南。希望這能讓別人的生活更輕鬆一些。

基本優化

在用C語言重寫Python原始碼之前,請考慮一下這些標準的Python優化。

內置數據結構

Python中內置的數據結構(如set和dict)是用C編寫的。它們比將你自己的數據結構編寫為Python類要快得多。除了標準的set、dict、list和tuple之外,其他數據結構都記錄在collections模塊中。

列表推導式

與其向一個列表附加內容,不如使用列表推導式。

ctypes

ctypes是一個模塊,它允許你通過Python代碼與C代碼進行通信,而不需要使用子進程或類似的模塊來從CLI運行另一個進程。

這有兩個部分:編譯要作為共享對象加載的C代碼,並在Python代碼中設置數據結構,從而映射到C-types。

在這篇文章中,我將把我的Python代碼連接到lcs.c,它可以找到兩個字符串列表的最長公共子序列。在Python代碼中,我想實現這樣的效果:

這個特殊的C函數的一些挑戰是函數籤名的參數類型是字符串列表,而返回類型的長度不是固定的。我用一個包含指針和長度的Sequence結構來解決這個問題。

為Python編譯C代碼

首先,C原始碼(lcs.c)被編譯為可以被Python裝載的lcs.so。

接下來,我們開始編寫Python代碼來使用這個共享對象文件。

Python中的結構體

下面,我將展示我的C原始碼中使用的兩個結構體。

在這裡,你可以看到結構體的Python翻譯。

一些註解:

所有結構體都是從ctypes.Structure繼承的class。

惟一的欄位是_fields_,它是一個元組列表。每個元組是(<variable-name>, <ctypes.TYPE>)。

ctypes還有像c_char (char)和c_char_p (*char)這樣的類型。

ctypes還包括POINTER(),它會從傳遞給它的任何類型創建一個指針類型。

如果你有一個類似於CELL中的遞歸定義,則你必須傳遞初始聲明,然後添加_fields_欄位以供以後引用。

由於我在我的Python代碼中沒有使用CELL,所以我不需要將這個結構體寫出來,但是它在遞歸欄位中有一個有趣的特性。

調用你的C代碼

另外,你需要一些代碼來將Python類型轉換為新的C結構。最後,你可以使用新的C函數來加速Python代碼。

更多註解:

**char(字符串列表)在Python中直接映射到字節列表。

lcs.c有一個函數lcs(),它的籤名是:struct Sequence *lcs(struct Sequence *s1, struct Sequence *s2)。要設置其返回類型,我使用lcsmodule.lcs.restype = ctypes.POINTER(SEQUENCE)。

為了調用一個對struct Sequence的引用,我使用ctypes.byref(),它返回一個指向你的對象的「輕量級指針」(比ctypes.POINTER()更快)。

common.items是一個字節列表,因此它們被解碼後得到的ret將是一個str列表。

lcsmodule.freeSequence(common)只是釋放與common相關的內存。這很重要,因為它不會被垃圾回收器(AFAIK)收集。

優化的Python代碼: 你用C編寫的代碼,並用Python為它編寫包裝器。

額外擴展: PyPy

註:我個人從未使用過PyPy。

一個簡單的優化就是在PyPy運行時中運行你的程序,其中包括一個即時(JIT)編譯器,當代碼循環多次運行時,它會通過將它們編譯成本機代碼來加速循環。

如果你有任何意見或想進一步討論,請發郵件給我。

[相關連結]:https://www.youtube.com/watch?v=SHbS9tYFpcQ

Sam Stevens, 2019年

[原始碼]:https://github.com/samuelstevens/personal-website

英文原文:https://samuelstevens.me/writing/optimizing-python-code-with-ctypes
譯者:野生大熊貓

相關焦點

  • python調用C++: ctypes庫教程
    一、準備C++代碼注意,使用ctypes庫調用C++代碼時,由於C++相比於C多了函數重載的特性,因此一個函數不能僅僅使用其名字來確定
  • Python:ctypes模塊調用go代碼
    可使用該模塊以純 Python 形式對這些庫進行封裝。注意事項:Python是利用ctypes來跟so模塊進行交互,其中存在著一個代碼的翻譯過程,包括數據類型的翻譯,如果需要傳參獲取接收返回值,需要在golang中將參數按照下表對應,定義成C語言的數據類型。
  • Python使用ctypes模塊調用DLL函數之C語言數組與numpy數組傳遞
    在Python語言中,可以使用ctypes模塊調用其它如C++語言編寫的動態連結庫DLL文件中的函數,在提高軟體運行效率的同時,也可以充分利用目前市面上各種第三方的DLL庫函數,以擴充Python軟體的功能及應用領域,減少重複編寫代碼、重複造輪子的工作量,這也充分體現了
  • python使用ctypes模塊調用DLL函數之傳遞數值、指針與字符串參數
    在Python語言中,可以使用ctypes模塊調用其它如C++語言編寫的動態連結庫DLL文件中的函數,在提高軟體運行效率的同時,也可以充分利用目前市面上各種第三方的DLL庫函數,以擴充Python軟體的功能及應用領域,減少重複編寫代碼、重複造輪子的工作量,這也充分體現了Python語言作為一種膠水語言所特有的優勢。
  • Python使用ctypes模塊調用DLL函數之傳遞結構體參數
    在Python語言中,可以使用ctypes模塊調用其它如C++語言編寫的動態連結庫DLL文件中的函數,在提高軟體運行效率的同時,也可以充分利用目前市面上各種第三方的DLL庫函數,以擴充Python軟體的功能及應用領域,減少重複編寫代碼、重複造輪子的工作量,這也充分體現了Python語言作為一種膠水語言所特有的優勢
  • python3使用ctypes在windows中訪問C和C++動態連結庫函數示例
    python3使用ctypes在windows中訪問C和C++動態連結庫函數示例這是我們的第一個示例,我們儘量簡單,不傳參,不返回,不訪問其他的動態連結庫一 測試環境介紹和準備二 C/C++部分代碼1 首先完成C/C++的動態連結庫,與做python擴展庫不同,ctypes調用的c++庫其實與python沒有代碼關聯,只是提供了開放公共標準。
  • Python使用ctypes模塊調用DLL函數之複數數組的參數傳遞
    在Python語言中,可以使用ctypes模塊調用其它如C++語言編寫的動態連結庫DLL文件中的函數,前面多篇文章中已經講了傳遞數值/指針/字符串參數、傳遞結構體參數、傳遞普通數組類型的例子,大家可以回看一下,這樣可以更好的理解本次要講的內容。
  • Python 性能優化的20條招數
    使用C擴展(Extension)目前主要有 CPython(python最常見的實現的方式)原生API, ctypes,Cython,cffi三種方式,它們的作用是使得 Python 程序可以調用由C編譯成的動態連結庫,其特點分別是
  • Python 通過 ctypes 調用 C 程序實例
    (點擊上方藍字,快速關注我們)來源:思誠之道http://www.bjhee.com/python-ctypes.html
  • Python性能優化的20條招數
    使用C擴展(Extension)目前主要有 CPython(python最常見的實現的方式)原生API, ctypes,Cython,cffi三種方式,它們的作用是使得 Python 程序可以調用由C編譯成的動態連結庫,其特點分別是:CPython 原生 API: 通過引入 Python.h 頭文件,對應的C程序中可以直接使用
  • Python遠程代碼執行漏洞(CVE-2021-3177)
    2021年02月19日,Python官方發布安全公告,公開了python中的一個RCE漏洞(CVE-2021-3177),其CVSSv3評分為9.8。 Python ctypes模塊是Python內建的用於調用動態連結庫函數的功能模塊。
  • Python安全編碼指南(一)
    0x02 Numbers —> ctypes, xrange, len, decimalctypectypes是Python的一個外部庫,提供和C語言兼容的數據類型,具體可見官方文檔測試代碼:因為len()函數沒有對對象的長度進行檢查,也沒有使用python int objects(使用了就會沒有限制),當對象可能包含一個「.length」屬性的時候,就有可能造成溢出錯誤。解決辦法同樣也是使用python int objects。
  • Python 性能優化
    但是,作為一個python開發者應該要pythonic,而且pythonic的代碼往往比non-pythonic的代碼效率高一些,比如:dict的iteritems 而不是items(同itervalues,iterkeys)使用generator,特別是在循環中可能提前break的情況基於profile的優化即使我們的代碼已經非常pythonic
  • 一行代碼讓你的Python運行速度提高100倍!Python真強!
    python一直被病垢運行速度太慢,但是實際上python的執行效率並不慢,慢的是python用的解釋器Cpython運行效率太差。「一行代碼讓python的運行速度提高100倍」這絕不是譁眾取寵的論調。我們來看一下這個最簡單的例子,從1一直累加到1億。
  • Why Python is Slow? Looking Under the Hood
    前言最近寫了點rankboost相關的代碼,發現當weaklearner比較多且數據量巨大的時候,單純的利用python+sklearn+numpy來fit是非常慢的,就想到了之前用過的cython,寫完之後果然效率飛起啊。但是為什麼python如此之慢呢?我這個菜雞還是需要學習一下的。。。Why python is slow?
  • 【超詳細 | Python】CS免殺-Shellcode Loader原理(python)
    上傳到主機,用loader加載shellcode我們在用cs生成payload時,會生成一段特定程式語言的代碼(以python為例)裡面一長串\xfc樣式的16進位代碼,這就是子彈shellcode但光有子彈不行,所以我們需要一把槍loader才能讓他發揮作用。
  • python+C、C++混合編程的應用
    python中使用ctypes moduel,將python類型轉成c/c++類型首先,編寫一段累加數值的c代碼:extern "C" { int addBuf(char* data, int num, char* outData);}int addBuf(char* data, int num
  • Python案例|混用C函數
    使用Python自帶的 ctypes 模塊在Python內直接調用C的動態連結庫代碼,這對於調用現有的庫,一些不開源的庫很有用.用wsig, sip, boost.python等這些老派方法,他們都有各自的專門的要求,小編沒用過,就不妄下評論了。 使用pybind11來寫Python擴展模塊。
  • 20招讓你的 Python 飛起來!
    使用C擴展(Extension)目前主要有CPython(python最常見的實現的方式)原生API, ctypes,Cython,cffi三種方式,它們的作用是使得Python程序可以調用由C編譯成的動態連結庫,其特點分別是:CPython原生API: 通過引入Python.h頭文件,對應的C程序中可以直接使用Python的數據結構。實現過程相對繁瑣,但是有比較大的適用範圍。
  • 加速程序開發 Python整合C語言模塊
    原始碼層面上的整合利用Python本身提供的ctypes模塊可以使Python語言和C語言在原始碼層面上進行整合。本節介紹了如何通過使用ctypes庫,在Python程序中可以定義類似C語言的變量。表1中的第一列是在ctypes庫中定義的變量類型,第二列是C語言定義的變量類型,第三列是Python語言在不使用ctypes時定義的變量類型。