0Pricing
Python Academy · レッスン

Python C 拡張モジュールの作成

Python/C API を使って、簡単な .so 拡張モジュールを作成します。

「Python C 拡張モジュールの作成」はCoddyKit上の無料Python Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPython Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Python Academyコースには全4レッスンが含まれています。

C拡張の構造

最小限のC拡張には、メソッド関数、メソッドテーブル、モジュール定義構造体、および PyInit_ エントリーポイントがあります。

// myext.c skeleton
#include <Python.h>

static PyObject* say_hello(PyObject* self, PyObject* args) {
    Py_RETURN_NONE;
}

static PyMethodDef methods[] = {
    {"say_hello", say_hello, METH_NOARGS, "Print hello"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef module = {
    PyModuleDef_HEAD_INIT, "myext", NULL, -1, methods
};

PyMODINIT_FUNC PyInit_myext(void) {
    return PyModule_Create(&module);
}

引数の解析

PyArg_ParseTuple(args, "ii", &a, &b) は、Pythonの引数をC変数に解析します。フォーマットコードには、i=int、d=double、s=char*、O=PyObject* があります。

static PyObject* add(PyObject* self, PyObject* args) {
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b))
        return NULL;
    return PyLong_FromLong(a + b);
}

値の返却

Pythonの戻り値は、PyLong_FromLong、PyFloat_FromDouble、PyUnicode_FromString、Py_BuildValue を使用して作成します。

// Return a Python tuple (int, float)
static PyObject* stats(PyObject* self, PyObject* args) {
    int n = 10;
    double avg = 5.0;
    return Py_BuildValue("(id)", n, avg);
    // Py_BuildValue format: i=int d=double s=str
}

参照カウント

すべてのPyObject*には参照カウントがあります。Py_INCREF/Py_DECREF は慎重に使用してください。C関数から返される戻り値の所有権は呼び出し側に移ります(stolen references)。

static PyObject* make_list(PyObject* self, PyObject* args) {
    PyObject* lst = PyList_New(3);
    for (int i = 0; i < 3; i++) {
        // PyList_SET_ITEM steals the reference:
        PyList_SET_ITEM(lst, i, PyLong_FromLong(i));
    }
    return lst;   // caller owns the list
}

例外の発生

CからPythonの例外を発生させるには、PyErr_SetString(PyExc_ValueError, "msg") を使用し、その後 NULL を返します。

static PyObject* safe_div(PyObject* self, PyObject* args) {
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL;
    if (b == 0) {
        PyErr_SetString(PyExc_ZeroDivisionError, "division by zero");
        return NULL;
    }
    return PyLong_FromLong(a / b);
}

C拡張用のsetup.py

Cのソースファイルを宣言するには setuptools.Extension を使用します。python setup.py build_ext --inplace でビルドします。

# setup.py
from setuptools import setup, Extension

setup(
    name="myext",
    ext_modules=[
        Extension(
            "myext",
            sources=["myext.c"],
            extra_compile_args=["-O2"],
        )
    ]
)
# python setup.py build_ext --inplace
# import myext; myext.add(3, 4)

ビルドした拡張の使用

ビルドが完了したら、通常のPythonモジュールと同じように拡張をインポートします。Pythonは myext.so(Windowsでは .pyd)を探して拡張を見つけます。

# After: python setup.py build_ext --inplace
import myext
print(myext.add(3, 4))        # 7
print(myext.safe_div(10, 2))  # 5
# myext.safe_div(1, 0)         # ZeroDivisionError

キーワード引数

C関数でキーワード引数をサポートするには、キーワードのリストとともに PyArg_ParseTupleAndKeywords を使用します。

static char* kwargs[] = {"x", "y", NULL};

static PyObject* hypot_c(PyObject* self,
                          PyObject* args,
                          PyObject* kw) {
    double x, y;
    if (!PyArg_ParseTupleAndKeywords(args, kw, "dd",
                                     kwargs, &x, &y))
        return NULL;
    return PyFloat_FromDouble(sqrt(x*x + y*y));
}

モジュールレベルの定数

PyModule_AddIntConstant を使用し、PyInit_ 内で整数または文字列の定数をモジュールに追加します。

PyMODINIT_FUNC PyInit_myext(void) {
    PyObject* m = PyModule_Create(&module);
    if (!m) return NULL;
    PyModule_AddIntConstant(m, "VERSION", 1);
    PyModule_AddStringConstant(m, "AUTHOR", "Alice");
    return m;
}

GILの解放

Cコードの実行中にほかのPythonスレッドを動作させるには、純粋なCの処理を Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS で囲みます。

static PyObject* heavy(PyObject* self, PyObject* args) {
    long n;
    if (!PyArg_ParseTuple(args, "l", &n)) return NULL;
    long result;
    Py_BEGIN_ALLOW_THREADS
        result = slow_c_computation(n);  // GIL released
    Py_END_ALLOW_THREADS
    return PyLong_FromLong(result);
}

拡張のテスト

C拡張は、pytestを使用してPythonインターフェース経由でテストします。専用のCテストツールは必要ありません。

# test_myext.py
import pytest
import myext

def test_add(): assert myext.add(3, 4) == 7
def test_zero_div():
    with pytest.raises(ZeroDivisionError):
        myext.safe_div(1, 0)

確認問題

Pythonの例外が設定されたことを示すには、C拡張関数は何を返す必要がありますか?

まとめ

C拡張には、メソッド関数(引数を解析し、PyObject*を返す)、メソッドテーブル、モジュール定義構造体、および PyInit_name() が必要です。PyErr_SetString で例外を設定し、NULLを返すことで例外を発生させます。純粋なCの処理ではGILを解放します。

よくある質問

「Python C 拡張モジュールの作成」レッスンは無料ですか?

はい。「Python C 拡張モジュールの作成」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Python Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Python Academyコースには全4レッスンが含まれています。

「Python C 拡張モジュールの作成」で何を学びますか?

Python/C API を使って、簡単な .so 拡張モジュールを作成します。 ブラウザで直接実行するハンズオンコードでPython Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Python Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPython Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「Python C 拡張モジュールの作成」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPython Academyレッスンでコードを書いて実行できますか?

はい。すべてのPython Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. C 拡張の理由: 用途とトレードオフ
  2. ctypes: Python から C ライブラリを呼び出す
  3. cffi: C 外部関数インターフェース
  4. Python C 拡張モジュールの作成
← Python Academyに戻る