當前位置:
首頁 > 知識 > 怎麼給 Python 寫 C 擴展?

怎麼給 Python 寫 C 擴展?


作者:nucpylab


原文:http://www.cnblogs.com/nucpylab/p/8608722.html



1. 環境準備


https://mp.weixin.qq.com/s/S2B85S8Yw5zcZgeqp-MqDA


如果是Linux只需要安裝Python3.x + Python-dev。


Windows下稍微複雜點,VS2017 + Python3.6.3


VS2017可用社區版,需要選擇安裝的環境如下:



2. Hello World !


2.1 C模塊封裝


以計算兩個數相加為例,選擇任意文件夾,新建如下C語言源碼:

  1. // 文件名 calc.c

  2. #include

  3. int

    add

    (

    int

    x

    ,

    int

    y

    ){

    // C 函數

  4.    

    return

    x

    +

    y

    ;

  5. }

  6. static

    PyObject

    *

    calc_add

    (

    PyObject

    *

    self

    ,

    PyObject

    *

    args

    ){

  7.    

    int

    x

    ,

    y

    ;

  8.    

    // Python傳入參數

  9.    

    // "ii" 表示傳入參數為2個int型參數,將其解析到x, y變數中

  10.    

    if

    (!

    PyArg_ParseTuple

    (

    args

    ,

    "ii"

    ,

    &

    x

    ,

    &

    y

    ))

  11.        

    return

    NULL

    ;

  12.    

    return

    PyLong_FromLong

    (

    add

    (

    x

    ,

    y

    ));

  13. }

  14. // 模塊的方法列表

  15. static

    PyMethodDef

    CalcMethods

    []

    =

    {

  16.    

    {

    "add"

    ,

    calc_add

    ,

    METH_VARARGS

    ,

    "函數描述"

    },

  17.    

    {

    NULL

    ,

    NULL

    ,

    0

    ,

    NULL

    }

  18. };

  19. // 模塊

  20. static

    struct

    PyModuleDef

    calcmodule

    =

    {

  21.    

    PyModuleDef_HEAD_INIT

    ,

  22.    

    "calc"

    ,

    // 模塊名

  23.    NULL

    ,

    // 模塊文檔

  24.    

    -

    1

    ,

         

    /* size of per-interpreter state of the module,

  25.                or -1 if the module keeps state in global variables. */

  26.    

    CalcMethods

  27. };

  28. // 初始化

  29. PyMODINIT_FUNC

    PyInit_calc

    (

    void

    )

  30. {

  31.    

    return

    PyModule_Create

    (&

    calcmodule

    );

  32. }


其中,

靜態函數

 calc_add 以python的C介面方式封裝了add函數,命名方式 

模塊名

_

函數名


靜態PyMethodDef列表

 變數 

CalcMethods

 包含了該模塊方法的描述


靜態struct PyModuleDef結構體

 變數 

calcmodule

 定義了模塊的描述


PyInit_calc

 函數初始化了模塊,命名方式 

PyInit_

模塊名


2.2 C源碼編譯


在VS2017中可以直接生成 .dll 文件,然後改名為 .pyd 就可在python程序中引入該模塊了,但是,這不"清真",正確的姿勢是寫一個 

setup

.

py

然後通過python調cl.exe編譯。


新建 

setup

.

py

文件,內容如下:

  1. # setup.py

  2. from

    distutils

    .

    core

    import

    setup

    ,

    Extension

  3. module1

    =

    Extension

    (

    "calc"

    ,

  4.                    sources

    =[

    "calc.c"

    ])

  5. setup

    (

    name

    =

    "calc_model"

    ,

  6.      version

    =

    "1.0"

    ,

  7.      description

    =

    "Hello ?"

    ,

  8.      ext_modules

    =[

    module1

    ]

  9. )


然後,從Windows的命令行(命令提示符)下進入到這個文件夾下,執行:


python setup

.

py build


即可完成編譯,如果出現某 

.

bat

文件未找到,說明你的VS沒有安裝相應的依賴(Linux下編譯不成功原因可能是沒有裝python-dev),按文章開頭給出的依賴庫添加修改(此時不需要重新安裝VS)。


編譯結束後,在該文件夾下會出現 

build

 文件夾,進入該文件夾,出現如下兩個文件夾:



進入 

lib.xxx

那個文件夾,裡面有個 

.pyd

 結尾的文件(Linux下為 

.so

 結尾),這就是我們編譯好的python模塊了,如下:



當然,你也可以改名為 

calc.pyd

 比較好看,不過這不影響調用。


2.3 Python調用


這部分就簡單了,進入含有編譯好的 

.pyd

 文件夾,新建如下文件:

  1. import

    calc

  2. print

    (

    calc

    .

    add

    (

    12

    ,

    21

    ))


這就是一個普通庫,這樣調用就OK了。


3. Python的參數傳遞以及C的返回值相關問題


這部分我直接甩出文件就行,編譯及調用過程與上面一樣。


C 文件

  1. /**構建返回值

  2. Py_BuildValue("")                        None

  3. Py_BuildValue("i", 123)                  123

  4. Py_BuildValue("iii", 123, 456, 789)      (123, 456, 789)

  5. Py_BuildValue("s", "hello")              "hello"

  6. Py_BuildValue("y", "hello")              b"hello"

  7. Py_BuildValue("ss", "hello", "world")    ("hello", "world")

  8. Py_BuildValue("s#", "hello", 4)          "hell"

  9. Py_BuildValue("y#", "hello", 4)          b"hell"

  10. Py_BuildValue("()")                      ()

  11. Py_BuildValue("(i)", 123)                (123,)

  12. Py_BuildValue("(ii)", 123, 456)          (123, 456)

  13. Py_BuildValue("(i,i)", 123, 456)         (123, 456)

  14. Py_BuildValue("[i,i]", 123, 456)         [123, 456]

  15. Py_BuildValue("{s:i,s:i}", "abc", 123, "def", 456)    {"abc": 123, "def": 456}

  16. Py_BuildValue("((ii)(ii)) (ii)", 1, 2, 3, 4, 5, 6)          (((1, 2), (3, 4)), (5, 6))

  17. **/

  18. #include

  19. static

    PyObject

    *

    value_commonArgs

    (

    PyObject

    *

    self

    ,

    PyObject

    *

    args

    ){

  20.    

    // 傳入普通參數,例如: s = value.com(1, 2.3, "Hello C")

  21.    

    int

    x

    ;

  22.    

    double

    y

    ;

  23.    

    char

    *

    z

    ;

  24.    

    if

    (!

    PyArg_ParseTuple

    (

    args

    ,

    "ids"

    ,

    &

    x

    ,

    &

    y

    ,

    &

    z

    ))

  25.        

    return

    NULL

    ;

  26.    printf

    (

    "The args is %d and %f and %s .n"

    ,

    x

    ,

    y

    ,

    z

    );

  27.    

    // 返回(x, y, z)的元組

  28.    

    return

    Py_BuildValue

    (

    "(i,d,s)"

    ,

    x

    ,

    y

    ,

    z

    );

  29. }

  30. static

    PyObject

    *

    value_tupleTest

    (

    PyObject

    *

    self

    ,

    PyObject

    *

    args

    ){

  31.    

    // t = value.tut((1, 3), "Tuple")

  32.    

    int

    x

    ,

    y

    ;

  33.    

    char

    *

    z

    ;

  34.    

    if

    (!

    PyArg_ParseTuple

    (

    args

    ,

    "(ii)s"

    ,

    &

    x

    ,

    &

    y

    ,

    &

    z

    ))

  35.        

    return

    NULL

    ;

  36.    printf

    (

    "The args is (%d, %d), %s .n"

    ,

    x

    ,

    y

    ,

    z

    );

  37.    

    // return ([1, 2], "hello")

  38.    

    return

    Py_BuildValue

    (

    "[i,i]s"

    ,

    x

    ,

    y

    ,

    z

    );

  39. }

  40. static

    PyObject

    *

    value_some

    (

    PyObject

    *

    self

    ,

    PyObject

    *

    args

    ){

  41.    

    /* 可選參數,可能是下面幾種, "|" 代表後面的參數可選

  42.        c = value.som(1)

  43.        value.som(1, 3)

  44.        value.som(1, 2, "hello")

  45.    */

  46.    

    int

    x

    =

    0

    ,

    y

    =

    0

    ;

  47.    

    char

    *

    z

    =

    NULL

    ;

  48.    

    if

    (!

    PyArg_ParseTuple

    (

    args

    ,

    "i|is"

    ,

    &

    x

    ,

    &

    y

    ,

    &

    z

    ))

  49.        

    return

    NULL

    ;

  50.    printf

    (

    "x is: %dn"

    ,

    x

    );

  51.    printf

    (

    "y is: %dn"

    ,

    y

    );

  52.    

    if

    (

    z

    !=

    NULL

    )

    printf

    (

    "z is: %sn"

    ,

    z

    );

  53.    

    return

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

在愛情中三觀正很重要嗎?
幸虧我都注釋掉了

TAG:Python開發 |