1
1

More than 3 years have passed since last update.

Python拡張の作り方

Posted at

C/C++ で Python 拡張を作る

準備

事前に、

  • apt install python3-dev
  • yum install python39-devel

等を実行し、Python の開発モジュールをインストールしてから実行してください。

C/C++ で Python 拡張を作成する場合

とりあえず、C++で myModule 作成する。

  • 必要なファイル

    • setup.py
    setup.py
    from distutils.core import setup, Extension
    
    setup(name = 'myModule', version = '1.0.0',  \
      ext_modules = [Extension('myModule', ['extention.cpp'])])
    
    • extention.cpp
    extention.cpp
    /*** ヘッダのインクルードパスにバージョンが入るので注意が必要です ***/
    #include <python3.7m/Python.h>
    
    static PyObject* c_helloworld(PyObject* self, PyObject* args)
    {
        printf("Hello Work ... Cpp\n");
        return Py_None;
    }
    
    /******************************************************************************/
    /* ここから下はPython拡張の定型的な記述になります。 */
    // myModule definition(python's name)
    static PyMethodDef myMethods[] = {
        { "hellowork", c_helloworld, METH_NOARGS, "Prints Hello Work" },
        { NULL }
    };
    
    // myModule definition struct
    static struct PyModuleDef myModule = {
        PyModuleDef_HEAD_INIT,
        "myModule",
        "Python3 C/C++ API Module Sample",
        -1,
        myMethods
    };
    
    // Initializes myModule
    PyMODINIT_FUNC PyInit_myModule(void)
    {
        return PyModule_Create(&myModule);
    }
    
  • ビルド&インストール

    $ python3 setup.py build
    $ sudo python3 setup.py install
    

    build は省略しても問題ないです。

  • 実行例

    >>> import myModule as m
    >>> m.hellowork()
    Hello Work ... Cpp
    

Python から引数をもらって戻り値を返す

特に意味はないですが、$a + b = c$ を行う Python拡張を、上記の extention.cpp に追加します。

  • 追加関数

    extention.cpp
    static PyObject* pyadd(PyObject* self, PyObject* args)
    {
        int a, b, c;
    
        // 関数引数が (int, int) なので "ii" を指定しています
        if (! PyArg_ParseTuple(args, "ii", &a, &b)){
            return NULL;
        }
    
        c = a + b;
    
        // 戻り値が int なので "i" を指定しています
        return Py_BuildValue("i", c);
    }
    
  • メソッド登録

    extention.cpp
    // myModule definition(python's name)
    static PyMethodDef myMethods[] = {
        { "hellowork", c_helloworld, METH_NOARGS, "Prints Hello Work" },
        { "add", pyadd, METH_VARARGS, "a + b = c"},  /* この行を追加 */
        { NULL }
    };
    
  • 実行例

    >>> import myModule as m
    >>> ret = m.add(33, 66)
    >>> ret
    99
    

戻り値としてリストを返す

  • 関数追加

    extention.cpp
    static PyObject* returnList(PyObject* self, PyObject* args)
    {
        return Py_BuildValue(
            "[[s,s,s,s],[i,i,i,i]]",
            "label1", "label2", "label3", "label4",
            1,2,3,4
        );
    }
    
  • 関数の登録

    extention.cpp
    // myModule definition(python's name)
    static PyMethodDef myMethods[] = {
        { "hellowork", c_helloworld, METH_NOARGS, "Prints Hello Work" },
        { "add", pyadd, METH_VARARGS, "a + b = c"},
        { "returnList", returnList, METH_NOARGS, "return List Sample" },  /* この行追加 */
        { NULL }
    };
    
  • 実行例

    >>> import myModule as m
    >>> ret = m.returnList()
    >>> ret[0]
    ['label1', 'label2', 'label3', 'label4']
    >>> ret[1]
    [1, 2, 3, 4]
    

参考サイト

Cython: C-Extensions for Python で Python 拡張を作る

基本形

事前に sudo pip3 install cython などを実行し、Cython をインストールしておいてください。
Cython を利用すると、Python の方が便利な処理は Python でコードを書き、C/C++ じゃないとダメなところだけ、C/C++ にすることができます。
Python はループ処理が遅い傾向があるため、大量のループを必要とする処理を C/C++ で実装することで高速化する、というような使い方をするらしい。

  • 必要なファイル

    • extention.pyx
    extention.pyx
    import cython
    
    # pythonから呼び出す関数
    def hellowork():
      print("Hello Work ... cython")
      return None
    
    • setup.py
    setup.py
    from distutils.core import setup, Extension
    from Cython.Build import cythonize
    
    ext = Extension("myModule2", sources=["extention.pyx"], include_dirs=['.'])
    setup(name="myModule2", ext_modules=cythonize([ext]))
    

ビルド&インストール方法、実行例は C/C++ と同様なので省略。

足し算の結果を返す

  • extention.pyx に追加する関数

    extention.pyx
    def add(int a, int b):
      cdef:
        int c;
    
      c = a + b
      return c
    

Cython で C 実装関数を実行する

  • extention.pyx に追加する import文、関数

    extention.pyx
    from cextention cimport c_hellowork_msg
    
    # ・・ #
    
    def cpp_hellowork():
      cpp_hellowork_msg()
      return None
    
  • cextention.pxd (新規追加)

    cextention.pxd
    cdef extern from "cextention.h":
      void c_hellowork_msg()
    
  • cextention.c (新規追加)

    cextention.c
    #include <stdio.h>
    
    void c_hellowork_msg()
    {
      printf("Hello Work ... c\n");
    }
    
  • cextention.h (新規追加)

    cextention.h
    #ifndef _CEXTENTION_H_
    #define _CEXTENTION_H_
    
    void c_hellowork_msg();
    
    #endif
    
  • setup.py (ビルド対象ファイル追加)

    sources"cextention.c" を追加します。

    setup.py
    from distutils.core import setup, Extension
    from Cython.Build import cythonize
    
    ext = Extension("myModule2", sources=["extention.pyx", "cextention.c"], include_dirs=['.'])
    setup(name="myModule2", ext_modules=cythonize([ext]))
    

その他

Cython で C++ 実装関数を実行する場合、C と同じ方法ではダメなようです。
いまいち、理解できていないので、その内、勉強し直して更新しようと思います。

参考サイト

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1