Python Academy · Урок

Написание расширения Python на C

Создайте простое расширение .so с помощью API Python/C.

Урок 4 из 413 шагов

«Написание расширения Python на C» — бесплатный урок Python Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения 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 передают вызывающему коду право владения (ссылки с передачей владения).

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
}

Возбуждение исключений

Используйте PyErr_SetString(PyExc_ValueError, "msg"), чтобы возбудить исключение Python из C, а затем верните 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);
}

setup.py для расширений на C

Используйте setuptools.Extension, чтобы объявить исходные файлы C. Выполните сборку с помощью 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 (или .pyd в Windows).

# 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

Именованные аргументы

Используйте PyArg_ParseTupleAndKeywords вместе со списком имён, чтобы поддерживать именованные аргументы в функциях C.

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

Константы уровня модуля

Добавляйте целочисленные или строковые константы в модуль в PyInit_ с помощью PyModule_AddIntConstant.

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 в Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS, чтобы другие потоки Python могли выполняться во время работы кода C.

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 через их интерфейс Python с помощью pytest. Специальные инструменты тестирования 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)

Быстрая проверка

Что должна вернуть функция расширения на C, чтобы сообщить об установленном исключении Python?

Итоги

Расширению на C нужны функции методов (разбор аргументов и возврат PyObject*), таблица методов, структура определения модуля и PyInit_name(). Возбуждайте исключения, устанавливая их с помощью PyErr_SetString и возвращая NULL. Освобождайте GIL для работы на чистом C.

Можно начать бесплатно

Изучай Python с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
76
Уроки
320

Часто задаваемые вопросы

Урок «Написание расширения Python на C» бесплатный?

Да — полный текст урока «Написание расширения Python на C» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Python Academy, подпишись на CoddyKit PRO. Курс Python Academy содержит 4 уроков всего.

Чему я научусь в уроке «Написание расширения Python на C»?

Создайте простое расширение .so с помощью API Python/C. Ты практикуешь Python Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Python Academy?

Предыдущий опыт не требуется. Python Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Написание расширения Python на C»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Python Academy?

Да. Каждый урок Python Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Зачем нужны расширения C: применение и компромиссы
  2. ctypes: вызов библиотек C из Python
  3. cffi: интерфейс внешних функций C
  4. Написание расширения Python на C
← Назад к Python Academy