> **Источник:** https://python-all.ru/3.15/extending/first-extension-module.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# Ваш первый модуль расширения C API

Это руководство проведёт вас через создание простого модуля расширения Python, написанного на C или C++.

We will use the low-level Python C API directly. For easier ways to create extension modules, see the [recommended third party tools](https://python-all.ru/3.15/c-api/intro.html#c-api-tools).

The tutorial assumes basic knowledge about Python: you should be able to define functions in Python code before starting to write them in C. See [The Python Tutorial](https://python-all.ru/3.15/tutorial/index.html#tutorial-index) for an introduction to Python itself.

Руководство должно быть доступно для любого, кто может написать простую библиотеку на C. Хотя мы упомянем несколько концепций, которые новичок в C не обязан знать, например функции `static` или объявления компоновки, понимание их не обязательно для успеха.

Мы сосредоточимся на том, чтобы дать вам «ощущение» того, что такое C API Python. Оно не научит вас важным концепциям, таким как обработка ошибок и подсчёт ссылок, которые рассматриваются в следующих главах.

Мы предполагаем, что вы используете Unix-подобную систему (включая macOS и Linux) или Windows. В других системах вам, возможно, потребуется скорректировать некоторые детали – например, имя системной команды.

Вам потребуется установить подходящий компилятор C и заголовочные файлы Python для разработки. В Linux заголовочные файлы часто находятся в пакете `python3-dev` или `python3-devel`.

You need to be able to install Python packages. This tutorial uses [pip](https://python-all.ru/3.15/extending/first-extension-module.html) (`pip install`), but you can substitute any tool that can build and install `pyproject.toml`-based projects, like [uv](https://python-all.ru/3.15/extending/first-extension-module.html) (`uv pip install`). Preferably, have a [virtual environment](https://python-all.ru/3.15/library/venv.html#venv-def) activated.

> **Примечание**
>
> В этом руководстве используются API, добавленные в CPython 3.15. Чтобы создать расширение, совместимое с более ранними версиями CPython, следуйте более ранней версии этой документации.
>
> В этом руководстве используется синтаксис C, добавленный в C11 и C++20. Если ваше расширение должно быть совместимо с более ранними стандартами, следуйте руководствам в документации для Python 3.14 и ниже.

## Что мы будем делать

Давайте создадим модуль расширения с именем `spam` [\[1\]](https://python-all.ru/3.15/extending/first-extension-module.html#why-spam), который будет включать интерфейс Python к функции стандартной библиотеки C `system()`. Эта функция определена в `stdlib.h`. Она принимает строку C в качестве аргумента, выполняет аргумент как системную команду и возвращает значение результата в виде целого числа. Страница руководства для `system()` может резюмировать это так:

```c
#include <stdlib.h>
int system(const char *command);
```

Обратите внимание, что, как и многие функции в стандартной библиотеке C, эта функция уже существует в Python. В реальных проектах используйте [`os.system()`](https://python-all.ru/3.15/library/os.html#os.system) или [`subprocess.run()`](https://python-all.ru/3.15/library/subprocess.html#subprocess.run) вместо модуля, который вы напишете здесь.

Эту функцию нужно вызывать из Python следующим образом:

```pycon
>>> import spam
>>> status = spam.system("whoami")
User Name
>>> status
0
```

> **Примечание**
>
> Системная команда `whoami` выводит имя пользователя. Она полезна в таких учебных пособиях, потому что имеет одно и то же имя в Unix и Windows.

## Начните с заголовков

Сначала создайте каталог для этого учебного пособия и перейдите в него в командной строке. Затем создайте файл с именем `spammodule.c` в вашем каталоге. [\[2\]](https://python-all.ru/3.15/extending/first-extension-module.html#why-spammodule)

В этом файле мы включим два заголовка: `Python.h` для всех объявлений C API Python и `stdlib.h` для функции `system()`. [\[3\]](https://python-all.ru/3.15/extending/first-extension-module.html#stdlib-h)

Добавьте следующие строки в `spammodule.c`:

```c
#include <Python.h>
#include <stdlib.h>     // для system()
```

Обязательно разместите `stdlib.h` и любые другие include-директивы стандартной библиотеки *после* `Python.h`. В некоторых системах Python может определять некоторые макросы препроцессора, которые влияют на стандартные заголовки.

## Запуск вашей сборочной утилиты

Только с подключенными заголовками ваше расширение ничего не делает. Тем не менее, сейчас самое время скомпилировать его и попробовать импортировать. Это позволит убедиться, что сборочная утилита работает, чтобы вы могли вносить и тестировать изменения по мере чтения остального текста.

Сам CPython не включает инструмент для сборки модулей расширения; рекомендуется использовать сторонний проект. В этом учебном пособии мы будем использовать [meson-python](https://python-all.ru/3.15/extending/first-extension-module.html). (Если вы хотите использовать другой, см. [Приложение: Другие сборочные утилиты](https://python-all.ru/3.15/extending/first-extension-module.html#first-extension-other-tools).)

`meson-python` требует определения «проекта» с помощью двух дополнительных файлов.

Сначала добавьте `pyproject.toml` с таким содержимым:

```toml
[build-system]
build-backend = 'mesonpy'
requires = ['meson-python']

[project]
# Информация о проекте-заполнителе
# (измените это перед распространением модуля)
name = 'sampleproject'
version = '0'
```

Затем создайте `meson.build`, содержащий следующее:

```meson
project('sampleproject', 'c')

py = import('python').find_installation(pure: false)

py.extension_module(
   'spam',          # имя импортируемого модуля Python
   'spammodule.c',  # файл исходного кода C
   install: true,
)
```

> **Примечание**
>
> Подробнее о настройках см. в [документации meson-python](https://python-all.ru/3.15/extending/first-extension-module.html).

Теперь выполните сборку и установку *проекта в текущем каталоге* (`.`) с помощью `pip`:

```sh
python -m pip -v install .
```

Опция `-v` (`--verbose`) заставляет `pip` показывать вывод компилятора, что часто бывает полезно во время разработки.

> **Совет**
>
> Если у вас не установлен `pip`, выполните `python -m ensurepip`, желательно в [виртуальном окружении](https://python-all.ru/3.15/library/venv.html#venv-def). (Или, если вы предпочитаете другой инструмент, который может собирать и устанавливать проекты на основе `pyproject.toml`, используйте его.)

Обратите внимание: эту команду нужно запускать заново каждый раз, когда вы изменяете расширение. В отличие от Python, в C есть явный этап компиляции.

Когда расширение скомпилировано и установлено, запустите Python и попробуйте импортировать его. Должна возникнуть следующая ошибка:

```pycon
>>> import spam
Traceback (most recent call last):
   ...
ImportError: dynamic module does not define module export function (PyModExport_spam or PyInit_spam)
```

## Хук экспорта модуля

Исключение, которое вы получили при попытке импортировать модуль, сообщает, что Python ищет «функцию экспорта модуля», также известную как [хук экспорта модуля](https://python-all.ru/3.15/c-api/extension-modules.html#extension-export-hook). Давайте определим его.

Сначала добавьте прототип под строками `#include`:

```c

PyMODEXPORT_FUNC PyModExport_spam(void);
```

> **Совет**
>
> Прототип не является строго обязательным, но некоторые современные компиляторы выдают предупреждения без него. Как правило, лучше добавить прототип, чем отключать предупреждение.

Макрос [`PyMODEXPORT_FUNC`](https://python-all.ru/3.15/c-api/extension-modules.html#c.PyMODEXPORT_FUNC) объявляет возвращаемый тип функции и добавляет все необходимые объявления связывания, чтобы функция была видима и доступна при загрузке CPython.

После прототипа добавьте саму функцию. Пока пусть она возвращает `NULL`:

```c
PyMODEXPORT_FUNC
PyModExport_spam(void)
{
   return NULL;
}
```

Скомпилируйте и загрузите модуль снова. На этот раз вы должны получить другую ошибку.

```pycon
>>> import spam
Traceback (most recent call last):
   ...
SystemError: module export hook for module 'spam' failed without setting an exception
```

Просто возвращать `NULL` *не* является корректным поведением для хука экспорта, и CPython сообщает об этом. Это хорошо – значит, CPython нашёл функцию! Теперь сделаем её полезной.

## Таблица слотов

Вместо `NULL` хук экспорта должен возвращать информацию, необходимую для создания модуля. Начнём с основ: имени и строки документации.

Информация должна быть определена в массиве записей [`PySlot`](https://python-all.ru/3.15/c-api/slots.html#c.PySlot), которые по сути являются парами ключ-значение. Определите этот массив прямо перед вашим экспортным хуком:

```c
PyABIInfo_VAR(abi_info);

static PySlot spam_slots[] = {
   PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
   PySlot_STATIC_DATA(Py_mod_name, "spam"),
   PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),
   PySlot_END
};
```

Макрос [`PySlot_STATIC_DATA`](https://python-all.ru/3.15/c-api/slots.html#c.PySlot_STATIC_DATA) используется, когда значение слота (здесь: `&abi_info`, `"spam"` и докстринг) является указателем на константные статически выделенные данные.

Макрос `PyABIInfo_VAR(abi_info);` и слот [`Py_mod_abi`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_abi) представляют собой шаблонный код, который помогает предотвратить аварию интерпретатора из-за расширений, скомпилированных для другой версии Python.

Для обоих [`Py_mod_name`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_name) и [`Py_mod_doc`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_doc) значениями являются C-строки – то есть байтовые массивы в кодировке UTF-8, оканчивающиеся нулевым символом NUL.

Обратите внимание на сторожевую запись `PySlot_END` в конце. Она отмечает конец массива. При её отсутствии возникнет неопределённое поведение.

Массив определён как `static` – то есть невидим за пределами этого файла `.c`. Это станет общей практикой. CPython нужен доступ только к экспортному хуку; все глобальные переменные и остальные функции должны быть `static`, чтобы не конфликтовать с другими расширениями.

Верните этот массив из вашего экспортного хука вместо `NULL`:

```c
PyMODEXPORT_FUNC
PyModExport_spam(void)
{
   return spam_slots;
}
```

Теперь перекомпилируйте и проверьте:

```pycon
>>> import spam
>>> print(spam)
<module 'spam' from '/home/encukou/dev/cpython/spam.so'>
```

У вас есть модуль расширения! Попробуйте `help(spam)`, чтобы увидеть докстринг.

Следующим шагом будет добавление функции.

## Предоставление функции

Чтобы предоставить C-функцию `system()` напрямую Python, потребуется написать слой связующего кода для преобразования аргументов из объектов Python в значения C и обратного преобразования возвращаемого значения C в Python.

Один из простейших способов написать связующий код – это функция «[`METH_O`](https://python-all.ru/3.15/c-api/structures.html#c.METH_O)», которая принимает два объекта Python и возвращает один. Все объекты Python – независимо от типа – представлены в C как указатели на структуру [`PyObject`](https://python-all.ru/3.15/c-api/structures.html#c.PyObject).

Добавьте такую функцию над массивом слотов:

```c
static PyObject *
spam_system(PyObject *self, PyObject *arg)
{
   Py_RETURN_NONE;
}
```

Пока мы игнорируем аргументы и используем макрос [`Py_RETURN_NONE`](https://python-all.ru/3.15/c-api/none.html#c.Py_RETURN_NONE), который разворачивается в оператор `return`, корректно возвращающий объект Python [`None`](https://python-all.ru/3.15/library/constants.html#None).

Перекомпилируйте ваше расширение, чтобы убедиться, что нет синтаксических ошибок. Мы ещё не добавили `spam_system` в модуль, поэтому вы можете получить предупреждение, что `spam_system` не используется.

### Определения методов

Чтобы предоставить C-функцию Python, потребуется указать несколько фрагментов информации в структуре, называемой [`PyMethodDef`](https://python-all.ru/3.15/c-api/structures.html#c.PyMethodDef) [\[4\]](https://python-all.ru/3.15/extending/first-extension-module.html#why-pymethoddef):

- `ml_name`: имя функции Python;
- `ml_doc`: докстринг;
- `ml_meth`: вызываемая C-функция; и
- `ml_flags`: набор флагов, описывающих детали, например, как аргументы Python передаются C-функции. Мы будем использовать [`METH_O`](https://python-all.ru/3.15/c-api/structures.html#c.METH_O) – флаг, соответствующий сигнатуре нашей функции `spam_system`.

Поскольку модули обычно создают несколько функций, эти определения нужно собрать в массив с нулевой сторожевой записью в конце. Добавьте этот массив сразу под функцией `spam_system`:

```c

static PyMethodDef spam_methods[] = {
    {
        .ml_name="system",
        .ml_meth=spam_system,
        .ml_flags=METH_O,
        .ml_doc="Execute a shell command.",
    },
    {NULL, NULL, 0, NULL}        /* Страж */
};
```

Как и в случае со слотами модуля, нулевая сторожевая запись отмечает конец массива.

Далее мы добавим метод в модуль. Добавьте слот [`Py_mod_methods`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_methods) в ваш массив [`PyMethodDef`](https://python-all.ru/3.15/c-api/structures.html#c.PyMethodDef):

```c

PyABIInfo_VAR(abi_info);

static PySlot spam_slots[] = {
    PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
    PySlot_STATIC_DATA(Py_mod_name, "spam"),
    PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),
    PySlot_STATIC_DATA(Py_mod_methods, spam_methods),
    PySlot_END
};
```

Снова перекомпилируйте ваше расширение и протестируйте его. Обязательно перезапустите интерпретатор Python, чтобы `import spam` загрузил новую версию модуля.

Теперь вы должны иметь возможность вызвать функцию:

```pycon
>>> import spam
>>> print(spam.system)
<built-in function system>
>>> print(spam.system('whoami'))
None
```

Обратите внимание, что наша `spam.system` пока не выполняет команду `whoami`; она только возвращает `None`.

Проверьте, что функция принимает ровно один аргумент, как указано флагом [`METH_O`](https://python-all.ru/3.15/c-api/structures.html#c.METH_O):

```pycon
>>> print(spam.system('too', 'many', 'arguments'))
Traceback (most recent call last):
   ...
TypeError: spam.system() takes exactly one argument (3 given)
```

## Возврат целого числа

Теперь давайте посмотрим на возвращаемое значение. Вместо `None` мы хотим, чтобы `spam.system` возвращала число – то есть объект Python [`int`](https://python-all.ru/3.15/library/functions.html#int). В конечном итоге это будет код выхода системной команды, но начнём с фиксированного значения, скажем, `3`.

Python C API предоставляет функцию для создания объекта Python [`int`](https://python-all.ru/3.15/library/functions.html#int) из значения C `int`: [`PyLong_FromLong()`](https://python-all.ru/3.15/c-api/long.html#c.PyLong_FromLong). [\[5\]](https://python-all.ru/3.15/extending/first-extension-module.html#why-pylongfromlong)

Чтобы вызвать его, замените `Py_RETURN_NONE` следующими тремя строками:

```c
static PyObject *
spam_system(PyObject *self, PyObject *arg)
{
   int status = 3;
   PyObject *result = PyLong_FromLong(status);
   return result;
}
```

Перекомпилируйте, снова перезапустите интерпретатор Python и проверьте, что функция теперь возвращает 3:

```pycon
>>> import spam
>>> spam.system('whoami')
3
```

## Принимает строку

Наконец, давайте обработаем аргумент функции.

Наша C-функция `spam_system()` принимает два аргумента. Первый, `PyObject *self`, будет установлен на объект модуля `spam`. В нашем случае это бесполезно, так что мы его проигнорируем.

Второй аргумент, `PyObject *arg`, будет установлен на объект, который пользователь передал из Python. Мы ожидаем, что это строка Python. Чтобы использовать содержащуюся в ней информацию, нам нужно преобразовать её в C-значение – в данном случае, C-строку (`const char *`).

Здесь есть небольшое несоответствие типов: объекты Python [`str`](https://python-all.ru/3.15/library/stdtypes.html#str) хранят текст Unicode, а C-строки – это массивы байтов. Поэтому нам нужно *закодировать* данные, и мы будем использовать кодировку UTF-8. (UTF-8 может не всегда подходить для системных команд, но это то, что [`str.encode()`](https://python-all.ru/3.15/library/stdtypes.html#str.encode) использует по умолчанию, и C API имеет специальную поддержку для неё.)

Функция для кодирования строки Python в буфер UTF-8 называется [`PyUnicode_AsUTF8AndSize()`](https://python-all.ru/3.15/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize) [\[6\]](https://python-all.ru/3.15/extending/first-extension-module.html#why-pyunicodeasutf8). Вызывайте её так:

```c
static PyObject *
spam_system(PyObject *self, PyObject *arg)
{
   const char *command = PyUnicode_AsUTF8AndSize(arg, NULL);
   int status = 3;
   PyObject *result = PyLong_FromLong(status);
   return result;
}
```

Если [`PyUnicode_AsUTF8AndSize()`](https://python-all.ru/3.15/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize) успешен, *command* будет указывать на полученную C-строку – байтовый массив, завершающийся нулём [\[7\]](https://python-all.ru/3.15/extending/first-extension-module.html#embedded-nul). Этот буфер управляется объектом *arg*, что означает, что нам не нужно освобождать его, но мы должны соблюдать некоторые правила:

- Мы должны использовать буфер только внутри функции `spam_system`. После возврата `spam_system` объект *arg* и управляемый им буфер могут быть собраны сборщиком мусора.
- Мы не должны изменять его. Именно поэтому мы используем `const`.

Если [`PyUnicode_AsUTF8AndSize()`](https://python-all.ru/3.15/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize) был *не* успешен, он возвращает указатель `NULL`. При вызове *любого* Python C API нам всегда нужно обрабатывать такие случаи ошибок. Общий способ делать это будет рассмотрен в следующих главах этой документации. Пока будьте уверены, что мы уже корректно обрабатываем ошибки от [`PyLong_FromLong()`](https://python-all.ru/3.15/c-api/long.html#c.PyLong_FromLong).

Для вызова [`PyUnicode_AsUTF8AndSize()`](https://python-all.ru/3.15/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize) правильный способ обработки ошибок – вернуть `NULL` из `spam_system`. Добавьте для этого блок `if`:

```c
static PyObject *
spam_system(PyObject *self, PyObject *arg)
{
   const char *command = PyUnicode_AsUTF8AndSize(arg);
   if (command == NULL) {
      return NULL;
   }
   int status = 3;
   PyObject *result = PyLong_FromLong(status);
   return result;
}
```

Чтобы проверить, что обработка ошибок работает, снова скомпилируйте, перезапустите Python, чтобы `import spam` подхватил новую версию вашего модуля, и попробуйте передать в функцию значение, не являющееся строкой:

```pycon
>>> import spam
>>> spam.system(3)
Traceback (most recent call last):
   ...
TypeError: bad argument type for built-in operation
```

Теперь осталось только вызвать функцию C-библиотеки `system()` с буфером `char *` и использовать её результат вместо `3`:

```c
static PyObject *
spam_system(PyObject *self, PyObject *arg)
{
   const char *command = PyUnicode_AsUTF8AndSize(arg);
   if (command == NULL) {
      return NULL;
   }
   int status = system(command);
   PyObject *result = PyLong_FromLong(status);
   return result;
}
```

Скомпилируйте модуль, перезапустите Python и протестируйте. На этот раз вы должны увидеть ваше имя пользователя – вывод системной команды `whoami`:

```pycon
>>> import spam
>>> result = spam.system('whoami')
User Name
>>> result
0
```

Вы также можете протестировать с другими командами, например `ls`, `dir` или несуществующей:

```pycon
>>> import spam
>>> result = spam.system('nonexistent-command')
sh: line 1: nonexistent-command: command not found
>>> result
32512
```

## Результат

Поздравляем! Вы написали полноценный модуль расширения Python C API и завершили этот учебник!

Вот полный исходный файл для вашего удобства:

```c
/// Включения

#include <Python.h>
#include <stdlib.h>     // для system()

/// Реализация spam.system

static PyObject *
spam_system(PyObject *self, PyObject *arg)
{
   const char *command = PyUnicode_AsUTF8AndSize(arg, NULL);
   if (command == NULL) {
      return NULL;
   }
   int status = system(command);
   PyObject *result = PyLong_FromLong(status);
   return result;
}

/// Таблица методов модуля

static PyMethodDef spam_methods[] = {
    {
        .ml_name="system",
        .ml_meth=spam_system,
        .ml_flags=METH_O,
        .ml_doc="Execute a shell command.",
    },
    {NULL, NULL, 0, NULL}        /* Страж */
};

/// Таблица слотов модуля

PyABIInfo_VAR(abi_info);

static PySlot spam_slots[] = {
    PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
    PySlot_STATIC_DATA(Py_mod_name, "spam"),
    PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),
    PySlot_STATIC_DATA(Py_mod_methods, spam_methods),
    PySlot_END
};

/// Прототип хука экспорта

PyMODEXPORT_FUNC PyModExport_spam(void);

/// Хук экспорта модуля

PyMODEXPORT_FUNC
PyModExport_spam(void)
{
   return spam_slots;
}
```

## Приложение: Другие инструменты сборки

Вы сможете следовать этому учебнику – за исключением самого раздела *Запуск вашего инструмента сборки* – с другим инструментом сборки, не `meson-python`.

Руководство пользователя по упаковке Python содержит [список рекомендуемых инструментов](https://python-all.ru/3.15/extending/first-extension-module.html); обязательно выберите тот, который подходит для языка C.

### Обходное решение для отсутствующей функции PyInit

Если вывод вашего инструмента сборки сообщает об отсутствии `PyInit_spam`, добавьте пока в свой модуль следующую функцию:

```c
// Обходное решение
void *PyInit_spam(void) { return NULL; }
```

Это заглушка для [функции инициализации](https://python-all.ru/3.15/c-api/extension-modules.html#extension-export-hook) старого образца, которая требовалась в модулях расширения для CPython 3.14 и ниже. Текущий CPython в ней не нуждается, но некоторые инструменты сборки могут по-прежнему предполагать, что все модули расширения должны её определять.

Если вы используете это обходное решение, вы получите исключение `SystemError: initialization of spam failed without raising an exception` вместо `ImportError: dynamic module does not define module export function`.

### Компиляция напрямую

Настоятельно рекомендуется использовать сторонний инструмент сборки, поскольку он позаботится о различных деталях вашей платформы и установки Python, именовании результирующего расширения и, позднее, о распространении вашей работы.

Если вы собираете расширение для *конкретной* системы или только для себя, вы, возможно, захотите запустить компилятор напрямую. Способ сделать это зависит от системы; будьте готовы к проблемам, которые придётся решать самостоятельно.

#### Linux

В Linux пакет разработки Python может включать команду `python3-config`, которая выводит необходимые флаги компилятора. Если вы её используете, проверьте, что она соответствует интерпретатору CPython, который вы будете использовать для загрузки модуля. Затем начните с следующей команды:

```sh
gcc --shared $(python3-config --cflags --ldflags) spammodule.c -o spam.so
```

Это должно создать файл `spam.so`, который нужно поместить в каталог [`sys.path`](https://python-all.ru/3.15/library/sys.html#sys.path).

Сноски

\[[1](https://python-all.ru/3.15/extending/first-extension-module.html#id1)\]

`spam` – любимая еда фанатов Монти Пайтона…

\[[2](https://python-all.ru/3.15/extending/first-extension-module.html#id2)\]

Имя исходного файла полностью на ваше усмотрение, хотя некоторые инструменты могут быть придирчивы к расширению `.c`. В этом руководстве используется традиционный суффикс `*module.c`. Некоторые используют `spam.c` для реализации модуля с именем `spam`, в проектах, где Python не является основным языком, могут использовать `py_spam.c`, и так далее.

\[[3](https://python-all.ru/3.15/extending/first-extension-module.html#id3)\]

Включать `stdlib.h` технически не обязательно, поскольку `Python.h` включает его и [несколько других стандартных заголовков](https://python-all.ru/3.15/c-api/intro.html#capi-system-includes) для собственных нужд или для обратной совместимости. Однако рекомендуется явно включать то, что нужно.

\[[4](https://python-all.ru/3.15/extending/first-extension-module.html#id4)\]

Структура `PyMethodDef` также используется для создания методов классов, поэтому отдельного «`PyFunctionDef`» не существует.

\[[5](https://python-all.ru/3.15/extending/first-extension-module.html#id5)\]

Название [`PyLong_FromLong()`](https://python-all.ru/3.15/c-api/long.html#c.PyLong_FromLong) может показаться неочевидным. `PyLong` относится к Python [`int`](https://python-all.ru/3.15/library/functions.html#int), который изначально назывался `long`; `FromLong` относится к типу C `long` (или `long int`).

\[[6](https://python-all.ru/3.15/extending/first-extension-module.html#id6)\]

Здесь `PyUnicode` относится к исходному названию класса Python [`str`](https://python-all.ru/3.15/library/stdtypes.html#str): `unicode`.

Часть названия `AndSize` указывает на то, что эта функция может также получать размер буфера через выходной аргумент. Нам это не нужно, поэтому устанавливаем второй аргумент в NULL.

\[[7](https://python-all.ru/3.15/extending/first-extension-module.html#id7)\]

Мы игнорируем тот факт, что строки Python могут содержать нулевые байты (NUL), которые завершают C-строку. Другими словами, наша функция будет считать `spam.system("foo\0bar")` `spam.system("foo")`. Эта возможность может привести к проблемам безопасности, поэтому настоящая функция `os.system` проверяет размер для такого случая и вызывает исключение.
