Python拡張の作り方

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

## 準備

事前に、

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

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


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

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

- 必要なファイル

  - setup.py

    ```python:setup.py
    from distutils.core import setup, Extension

    setup(name = 'myModule', version = '1.0.0',  \
      ext_modules = [Extension('myModule', ['extention.cpp'])])
    ```

  - extention.cpp

    ```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);
    }
    ```

- ビルド＆インストール

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

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

- 実行例

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


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

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

- 追加関数

    ```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);
    }
    ```

- メソッド登録

    ```cpp: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 }
    };
    ```

- 実行例

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


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

- 関数追加

    ```cpp: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
        );
    }
    ```

- 関数の登録

    ```cpp: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 }
    };
    ```

- 実行例

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

### 参考サイト

- [【Python C API入門】C/C++で拡張モジュール作ってPythonから呼ぶ -前編-｜はやぶさの技術ノート](https://cpp-learning.com/python_c_api_step1/)



# [Cython: C-Extensions for Python](https://cython.org/) で Python 拡張を作る

## 基本形

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

- 必要なファイル

  - extention.pyx

    ```python:extention.pyx
    import cython

    # pythonから呼び出す関数
    def hellowork():
      print("Hello Work ... cython")
      return None
    ```

  - setup.py

    ```python: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 に追加する関数

    ```python:extention.pyx
    def add(int a, int b):
      cdef:
        int c;

      c = a + b
      return c
    ```


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

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

    ```python:extention.pyx
    from cextention cimport c_hellowork_msg

    # ・・ #

    def cpp_hellowork():
      cpp_hellowork_msg()
      return None
    ```

- cextention.pxd (新規追加)

    ```python:cextention.pxd
    cdef extern from "cextention.h":
      void c_hellowork_msg()
    ```

- cextention.c (新規追加)

    ```c:cextention.c
    #include <stdio.h>

    void c_hellowork_msg()
    {
      printf("Hello Work ... c\n");
    }
    ```

- cextention.h (新規追加)

    ```c:cextention.h
    #ifndef _CEXTENTION_H_
    #define _CEXTENTION_H_

    void c_hellowork_msg();

    #endif
    ```

- setup.py (ビルド対象ファイル追加)

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

    ```python: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 と同じ方法ではダメなようです。
いまいち、理解できていないので、その内、勉強し直して更新しようと思います。


### 参考サイト

- [cython入門 - Qiita](https://qiita.com/en3/items/1f1a609c4d7c8f3066a7)
- [Cython で Python ⇔ C++ のインタフェースを作る](https://kazuki-nagasawa.hatenablog.com/entry/memo_20180508_boost_numpy_retval)