Документация Python неофициальный перевод

first-extension-module.md

552 строк · 36.4 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.16/extending/first-extension-module.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Ваш первый модуль расширения C API89Это руководство проведёт вас через создание простого модуля расширения Python, написанного на C или C++.1011We 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.16/c-api/intro.html#c-api-tools).1213The 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.16/tutorial/index.html#tutorial-index) for an introduction to Python itself.1415Руководство должно быть доступно для любого, кто может написать простую библиотеку на C. Хотя мы упомянем несколько концепций, которые новичок в C не обязан знать, например функции `static` или объявления компоновки, понимание их не обязательно для успеха.1617Мы сосредоточимся на том, чтобы дать вам «ощущение» того, что такое C API Python. Оно не научит вас важным концепциям, таким как обработка ошибок и подсчёт ссылок, которые рассматриваются в следующих главах.1819Мы предполагаем, что вы используете Unix-подобную систему (включая macOS и Linux) или Windows. В других системах вам, возможно, потребуется скорректировать некоторые детали – например, имя системной команды.2021Вам потребуется установить подходящий компилятор C и заголовочные файлы Python для разработки. В Linux заголовочные файлы часто находятся в пакете `python3-dev` или `python3-devel`.2223You need to be able to install Python packages. This tutorial uses [pip](https://python-all.ru/3.16/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.16/extending/first-extension-module.html) (`uv pip install`). Preferably, have a [virtual environment](https://python-all.ru/3.16/library/venv.html#venv-def) activated.2425> **Примечание**26>27> В этом руководстве используются API, добавленные в CPython 3.15. Чтобы создать расширение, совместимое с более ранними версиями CPython, следуйте более ранней версии этой документации.28>29> В этом руководстве используется синтаксис C, добавленный в C11 и C++20. Если ваше расширение должно быть совместимо с более ранними стандартами, следуйте руководствам в документации для Python 3.14 и ниже.3031## Что мы будем делать3233Давайте создадим модуль расширения с именем `spam` [\[1\]](https://python-all.ru/3.16/extending/first-extension-module.html#why-spam), который будет включать интерфейс Python к функции стандартной библиотеки C `system()`. Эта функция определена в `stdlib.h`. Она принимает строку C в качестве аргумента, выполняет аргумент как системную команду и возвращает значение результата в виде целого числа. Страница руководства для `system()` может резюмировать это так:3435```c36#include <stdlib.h>37int system(const char *command);38```3940Обратите внимание, что, как и многие функции в стандартной библиотеке C, эта функция уже существует в Python. В реальных проектах используйте [`os.system()`](https://python-all.ru/3.16/library/os.html#os.system) или [`subprocess.run()`](https://python-all.ru/3.16/library/subprocess.html#subprocess.run) вместо модуля, который вы напишете здесь.4142Эту функцию нужно вызывать из Python следующим образом:4344```pycon45>>> import spam46>>> status = spam.system("whoami")47User Name48>>> status49050```5152> **Примечание**53>54> Системная команда `whoami` выводит имя пользователя. Она полезна в таких учебных пособиях, потому что имеет одно и то же имя в Unix и Windows.5556## Начните с заголовков5758Сначала создайте каталог для этого учебного пособия и перейдите в него в командной строке. Затем создайте файл с именем `spammodule.c` в вашем каталоге. [\[2\]](https://python-all.ru/3.16/extending/first-extension-module.html#why-spammodule)5960В этом файле мы включим два заголовка: `Python.h` для всех объявлений C API Python и `stdlib.h` для функции `system()`. [\[3\]](https://python-all.ru/3.16/extending/first-extension-module.html#stdlib-h)6162Добавьте следующие строки в `spammodule.c`:6364```c65#include <Python.h>66#include <stdlib.h>     // для system()67```6869Обязательно разместите `stdlib.h` и любые другие include-директивы стандартной библиотеки *после* `Python.h`. В некоторых системах Python может определять некоторые макросы препроцессора, которые влияют на стандартные заголовки.7071## Запуск вашей сборочной утилиты7273Только с подключенными заголовками ваше расширение ничего не делает. Тем не менее, сейчас самое время скомпилировать его и попробовать импортировать. Это позволит убедиться, что сборочная утилита работает, чтобы вы могли вносить и тестировать изменения по мере чтения остального текста.7475Сам CPython не включает инструмент для сборки модулей расширения; рекомендуется использовать сторонний проект. В этом учебном пособии мы будем использовать [meson-python](https://python-all.ru/3.16/extending/first-extension-module.html). (Если вы хотите использовать другой, см. [Приложение: Другие сборочные утилиты](https://python-all.ru/3.16/extending/first-extension-module.html#first-extension-other-tools).)7677`meson-python` требует определения «проекта» с помощью двух дополнительных файлов.7879Сначала добавьте `pyproject.toml` с таким содержимым:8081```toml82[build-system]83build-backend = 'mesonpy'84requires = ['meson-python']8586[project]87# Информация о проекте-заполнителе88# (измените это перед распространением модуля)89name = 'sampleproject'90version = '0'91```9293Затем создайте `meson.build`, содержащий следующее:9495```meson96project('sampleproject', 'c')9798py = import('python').find_installation(pure: false)99100py.extension_module(101   'spam',          # имя импортируемого модуля Python102   'spammodule.c',  # файл исходного кода C103   install: true,104)105```106107> **Примечание**108>109> Подробнее о настройках см. в [документации meson-python](https://python-all.ru/3.16/extending/first-extension-module.html).110111Теперь выполните сборку и установку *проекта в текущем каталоге* (`.`) с помощью `pip`:112113```sh114python -m pip -v install .115```116117Опция `-v` (`--verbose`) заставляет `pip` показывать вывод компилятора, что часто бывает полезно во время разработки.118119> **Совет**120>121> Если у вас не установлен `pip`, выполните `python -m ensurepip`, желательно в [виртуальном окружении](https://python-all.ru/3.16/library/venv.html#venv-def). (Или, если вы предпочитаете другой инструмент, который может собирать и устанавливать проекты на основе `pyproject.toml`, используйте его.)122123Обратите внимание: эту команду нужно запускать заново каждый раз, когда вы изменяете расширение. В отличие от Python, в C есть явный этап компиляции.124125Когда расширение скомпилировано и установлено, запустите Python и попробуйте импортировать его. Должна возникнуть следующая ошибка:126127```pycon128>>> import spam129Traceback (most recent call last):130   ...131ImportError: dynamic module does not define module export function (PyModExport_spam or PyInit_spam)132```133134## Хук экспорта модуля135136Исключение, которое вы получили при попытке импортировать модуль, сообщает, что Python ищет «функцию экспорта модуля», также известную как [хук экспорта модуля](https://python-all.ru/3.16/c-api/extension-modules.html#extension-export-hook). Давайте определим его.137138Сначала добавьте прототип под строками `#include`:139140```c141142PyMODEXPORT_FUNC PyModExport_spam(void);143```144145> **Совет**146>147> Прототип не является строго обязательным, но некоторые современные компиляторы выдают предупреждения без него. Как правило, лучше добавить прототип, чем отключать предупреждение.148149Макрос [`PyMODEXPORT_FUNC`](https://python-all.ru/3.16/c-api/extension-modules.html#c.PyMODEXPORT_FUNC) объявляет возвращаемый тип функции и добавляет все необходимые объявления связывания, чтобы функция была видима и доступна при загрузке CPython.150151После прототипа добавьте саму функцию. Пока пусть она возвращает `NULL`:152153```c154PyMODEXPORT_FUNC155PyModExport_spam(void)156{157   return NULL;158}159```160161Скомпилируйте и загрузите модуль снова. На этот раз вы должны получить другую ошибку.162163```pycon164>>> import spam165Traceback (most recent call last):166   ...167SystemError: module export hook for module 'spam' failed without setting an exception168```169170Просто возвращать `NULL` *не* является корректным поведением для хука экспорта, и CPython сообщает об этом. Это хорошо – значит, CPython нашёл функцию! Теперь сделаем её полезной.171172## Таблица слотов173174Вместо `NULL` хук экспорта должен возвращать информацию, необходимую для создания модуля. Начнём с основ: имени и строки документации.175176Информация должна быть определена в массиве записей [`PySlot`](https://python-all.ru/3.16/c-api/slots.html#c.PySlot), которые по сути являются парами ключ-значение. Определите этот массив прямо перед вашим экспортным хуком:177178```c179PyABIInfo_VAR(abi_info);180181static PySlot spam_slots[] = {182   PySlot_STATIC_DATA(Py_mod_abi, &abi_info),183   PySlot_STATIC_DATA(Py_mod_name, "spam"),184   PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),185   PySlot_END186};187```188189Макрос [`PySlot_STATIC_DATA`](https://python-all.ru/3.16/c-api/slots.html#c.PySlot_STATIC_DATA) используется, когда значение слота (здесь: `&abi_info`, `"spam"` и докстринг) является указателем на константные статически выделенные данные.190191Макрос `PyABIInfo_VAR(abi_info);` и слот [`Py_mod_abi`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_abi) представляют собой шаблонный код, который помогает предотвратить аварию интерпретатора из-за расширений, скомпилированных для другой версии Python.192193Для обоих [`Py_mod_name`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_name) и [`Py_mod_doc`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_doc) значениями являются C-строки – то есть байтовые массивы в кодировке UTF-8, оканчивающиеся нулевым символом NUL.194195Обратите внимание на сторожевую запись `PySlot_END` в конце. Она отмечает конец массива. При её отсутствии возникнет неопределённое поведение.196197Массив определён как `static` – то есть невидим за пределами этого файла `.c`. Это станет общей практикой. CPython нужен доступ только к экспортному хуку; все глобальные переменные и остальные функции должны быть `static`, чтобы не конфликтовать с другими расширениями.198199Верните этот массив из вашего экспортного хука вместо `NULL`:200201```c202PyMODEXPORT_FUNC203PyModExport_spam(void)204{205   return spam_slots;206}207```208209Теперь перекомпилируйте и проверьте:210211```pycon212>>> import spam213>>> print(spam)214<module 'spam' from '/home/encukou/dev/cpython/spam.so'>215```216217У вас есть модуль расширения! Попробуйте `help(spam)`, чтобы увидеть докстринг.218219Следующим шагом будет добавление функции.220221## Предоставление функции222223Чтобы предоставить C-функцию `system()` напрямую Python, потребуется написать слой связующего кода для преобразования аргументов из объектов Python в значения C и обратного преобразования возвращаемого значения C в Python.224225Один из простейших способов написать связующий код – это функция «[`METH_O`](https://python-all.ru/3.16/c-api/structures.html#c.METH_O)», которая принимает два объекта Python и возвращает один. Все объекты Python – независимо от типа – представлены в C как указатели на структуру [`PyObject`](https://python-all.ru/3.16/c-api/structures.html#c.PyObject).226227Добавьте такую функцию над массивом слотов:228229```c230static PyObject *231spam_system(PyObject *self, PyObject *arg)232{233   Py_RETURN_NONE;234}235```236237Пока мы игнорируем аргументы и используем макрос [`Py_RETURN_NONE`](https://python-all.ru/3.16/c-api/none.html#c.Py_RETURN_NONE), который разворачивается в оператор `return`, корректно возвращающий объект Python [`None`](https://python-all.ru/3.16/library/constants.html#None).238239Перекомпилируйте ваше расширение, чтобы убедиться, что нет синтаксических ошибок. Мы ещё не добавили `spam_system` в модуль, поэтому вы можете получить предупреждение, что `spam_system` не используется.240241### Определения методов242243Чтобы предоставить C-функцию Python, потребуется указать несколько фрагментов информации в структуре, называемой [`PyMethodDef`](https://python-all.ru/3.16/c-api/structures.html#c.PyMethodDef) [\[4\]](https://python-all.ru/3.16/extending/first-extension-module.html#why-pymethoddef):244245- `ml_name`: имя функции Python;246- `ml_doc`: докстринг;247- `ml_meth`: вызываемая C-функция; и248- `ml_flags`: набор флагов, описывающих детали, например, как аргументы Python передаются C-функции. Мы будем использовать [`METH_O`](https://python-all.ru/3.16/c-api/structures.html#c.METH_O) – флаг, соответствующий сигнатуре нашей функции `spam_system`.249250Поскольку модули обычно создают несколько функций, эти определения нужно собрать в массив с нулевой сторожевой записью в конце. Добавьте этот массив сразу под функцией `spam_system`:251252```c253254static PyMethodDef spam_methods[] = {255    {256        .ml_name="system",257        .ml_meth=spam_system,258        .ml_flags=METH_O,259        .ml_doc="Execute a shell command.",260    },261    {NULL, NULL, 0, NULL}        /* Страж */262};263```264265Как и в случае со слотами модуля, нулевая сторожевая запись отмечает конец массива.266267Далее мы добавим метод в модуль. Добавьте слот [`Py_mod_methods`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_methods) в ваш массив [`PyMethodDef`](https://python-all.ru/3.16/c-api/structures.html#c.PyMethodDef):268269```c270271PyABIInfo_VAR(abi_info);272273static PySlot spam_slots[] = {274    PySlot_STATIC_DATA(Py_mod_abi, &abi_info),275    PySlot_STATIC_DATA(Py_mod_name, "spam"),276    PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),277    PySlot_STATIC_DATA(Py_mod_methods, spam_methods),278    PySlot_END279};280```281282Снова перекомпилируйте ваше расширение и протестируйте его. Обязательно перезапустите интерпретатор Python, чтобы `import spam` загрузил новую версию модуля.283284Теперь вы должны иметь возможность вызвать функцию:285286```pycon287>>> import spam288>>> print(spam.system)289<built-in function system>290>>> print(spam.system('whoami'))291None292```293294Обратите внимание, что наша `spam.system` пока не выполняет команду `whoami`; она только возвращает `None`.295296Проверьте, что функция принимает ровно один аргумент, как указано флагом [`METH_O`](https://python-all.ru/3.16/c-api/structures.html#c.METH_O):297298```pycon299>>> print(spam.system('too', 'many', 'arguments'))300Traceback (most recent call last):301   ...302TypeError: spam.system() takes exactly one argument (3 given)303```304305## Возврат целого числа306307Теперь давайте посмотрим на возвращаемое значение. Вместо `None` мы хотим, чтобы `spam.system` возвращала число – то есть объект Python [`int`](https://python-all.ru/3.16/library/functions.html#int). В конечном итоге это будет код выхода системной команды, но начнём с фиксированного значения, скажем, `3`.308309Python C API предоставляет функцию для создания объекта Python [`int`](https://python-all.ru/3.16/library/functions.html#int) из значения C `int`: [`PyLong_FromLong()`](https://python-all.ru/3.16/c-api/long.html#c.PyLong_FromLong). [\[5\]](https://python-all.ru/3.16/extending/first-extension-module.html#why-pylongfromlong)310311Чтобы вызвать его, замените `Py_RETURN_NONE` следующими тремя строками:312313```c314static PyObject *315spam_system(PyObject *self, PyObject *arg)316{317   int status = 3;318   PyObject *result = PyLong_FromLong(status);319   return result;320}321```322323Перекомпилируйте, снова перезапустите интерпретатор Python и проверьте, что функция теперь возвращает 3:324325```pycon326>>> import spam327>>> spam.system('whoami')3283329```330331## Принимает строку332333Наконец, давайте обработаем аргумент функции.334335Наша C-функция `spam_system()` принимает два аргумента. Первый, `PyObject *self`, будет установлен на объект модуля `spam`. В нашем случае это бесполезно, так что мы его проигнорируем.336337Второй аргумент, `PyObject *arg`, будет установлен на объект, который пользователь передал из Python. Мы ожидаем, что это строка Python. Чтобы использовать содержащуюся в ней информацию, нам нужно преобразовать её в C-значение – в данном случае, C-строку (`const char *`).338339Здесь есть небольшое несоответствие типов: объекты Python [`str`](https://python-all.ru/3.16/library/stdtypes.html#str) хранят текст Unicode, а C-строки – это массивы байтов. Поэтому нам нужно *закодировать* данные, и мы будем использовать кодировку UTF-8. (UTF-8 может не всегда подходить для системных команд, но это то, что [`str.encode()`](https://python-all.ru/3.16/library/stdtypes.html#str.encode) использует по умолчанию, и C API имеет специальную поддержку для неё.)340341Функция для кодирования строки Python в буфер UTF-8 называется [`PyUnicode_AsUTF8AndSize()`](https://python-all.ru/3.16/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize) [\[6\]](https://python-all.ru/3.16/extending/first-extension-module.html#why-pyunicodeasutf8). Вызывайте её так:342343```c344static PyObject *345spam_system(PyObject *self, PyObject *arg)346{347   const char *command = PyUnicode_AsUTF8AndSize(arg, NULL);348   int status = 3;349   PyObject *result = PyLong_FromLong(status);350   return result;351}352```353354Если [`PyUnicode_AsUTF8AndSize()`](https://python-all.ru/3.16/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize) успешен, *command* будет указывать на полученную C-строку – байтовый массив, завершающийся нулём [\[7\]](https://python-all.ru/3.16/extending/first-extension-module.html#embedded-nul). Этот буфер управляется объектом *arg*, что означает, что нам не нужно освобождать его, но мы должны соблюдать некоторые правила:355356- Мы должны использовать буфер только внутри функции `spam_system`. После возврата `spam_system` объект *arg* и управляемый им буфер могут быть собраны сборщиком мусора.357- Мы не должны изменять его. Именно поэтому мы используем `const`.358359Если [`PyUnicode_AsUTF8AndSize()`](https://python-all.ru/3.16/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize) был *не* успешен, он возвращает указатель `NULL`. При вызове *любого* Python C API нам всегда нужно обрабатывать такие случаи ошибок. Общий способ делать это будет рассмотрен в следующих главах этой документации. Пока будьте уверены, что мы уже корректно обрабатываем ошибки от [`PyLong_FromLong()`](https://python-all.ru/3.16/c-api/long.html#c.PyLong_FromLong).360361Для вызова [`PyUnicode_AsUTF8AndSize()`](https://python-all.ru/3.16/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize) правильный способ обработки ошибок – вернуть `NULL` из `spam_system`. Добавьте для этого блок `if`:362363```c364static PyObject *365spam_system(PyObject *self, PyObject *arg)366{367   const char *command = PyUnicode_AsUTF8AndSize(arg);368   if (command == NULL) {369      return NULL;370   }371   int status = 3;372   PyObject *result = PyLong_FromLong(status);373   return result;374}375```376377Чтобы проверить, что обработка ошибок работает, снова скомпилируйте, перезапустите Python, чтобы `import spam` подхватил новую версию вашего модуля, и попробуйте передать в функцию значение, не являющееся строкой:378379```pycon380>>> import spam381>>> spam.system(3)382Traceback (most recent call last):383   ...384TypeError: bad argument type for built-in operation385```386387Теперь осталось только вызвать функцию C-библиотеки `system()` с буфером `char *` и использовать её результат вместо `3`:388389```c390static PyObject *391spam_system(PyObject *self, PyObject *arg)392{393   const char *command = PyUnicode_AsUTF8AndSize(arg);394   if (command == NULL) {395      return NULL;396   }397   int status = system(command);398   PyObject *result = PyLong_FromLong(status);399   return result;400}401```402403Скомпилируйте модуль, перезапустите Python и протестируйте. На этот раз вы должны увидеть ваше имя пользователя – вывод системной команды `whoami`:404405```pycon406>>> import spam407>>> result = spam.system('whoami')408User Name409>>> result4100411```412413Вы также можете протестировать с другими командами, например `ls`, `dir` или несуществующей:414415```pycon416>>> import spam417>>> result = spam.system('nonexistent-command')418sh: line 1: nonexistent-command: command not found419>>> result42032512421```422423## Результат424425Поздравляем! Вы написали полноценный модуль расширения Python C API и завершили этот учебник!426427Вот полный исходный файл для вашего удобства:428429```c430/// Включения431432#include <Python.h>433#include <stdlib.h>     // для system()434435/// Реализация spam.system436437static PyObject *438spam_system(PyObject *self, PyObject *arg)439{440   const char *command = PyUnicode_AsUTF8AndSize(arg, NULL);441   if (command == NULL) {442      return NULL;443   }444   int status = system(command);445   PyObject *result = PyLong_FromLong(status);446   return result;447}448449/// Таблица методов модуля450451static PyMethodDef spam_methods[] = {452    {453        .ml_name="system",454        .ml_meth=spam_system,455        .ml_flags=METH_O,456        .ml_doc="Execute a shell command.",457    },458    {NULL, NULL, 0, NULL}        /* Страж */459};460461/// Таблица слотов модуля462463PyABIInfo_VAR(abi_info);464465static PySlot spam_slots[] = {466    PySlot_STATIC_DATA(Py_mod_abi, &abi_info),467    PySlot_STATIC_DATA(Py_mod_name, "spam"),468    PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),469    PySlot_STATIC_DATA(Py_mod_methods, spam_methods),470    PySlot_END471};472473/// Прототип хука экспорта474475PyMODEXPORT_FUNC PyModExport_spam(void);476477/// Хук экспорта модуля478479PyMODEXPORT_FUNC480PyModExport_spam(void)481{482   return spam_slots;483}484```485486## Приложение: Другие инструменты сборки487488Вы сможете следовать этому учебнику – за исключением самого раздела *Запуск вашего инструмента сборки* – с другим инструментом сборки, не `meson-python`.489490Руководство пользователя по упаковке Python содержит [список рекомендуемых инструментов](https://python-all.ru/3.16/extending/first-extension-module.html); обязательно выберите тот, который подходит для языка C.491492### Обходное решение для отсутствующей функции PyInit493494Если вывод вашего инструмента сборки сообщает об отсутствии `PyInit_spam`, добавьте пока в свой модуль следующую функцию:495496```c497// Обходное решение498void *PyInit_spam(void) { return NULL; }499```500501Это заглушка для [функции инициализации](https://python-all.ru/3.16/c-api/extension-modules.html#extension-export-hook) старого образца, которая требовалась в модулях расширения для CPython 3.14 и ниже. Текущий CPython в ней не нуждается, но некоторые инструменты сборки могут по-прежнему предполагать, что все модули расширения должны её определять.502503Если вы используете это обходное решение, вы получите исключение `SystemError: initialization of spam failed without raising an exception` вместо `ImportError: dynamic module does not define module export function`.504505### Компиляция напрямую506507Настоятельно рекомендуется использовать сторонний инструмент сборки, поскольку он позаботится о различных деталях вашей платформы и установки Python, именовании результирующего расширения и, позднее, о распространении вашей работы.508509Если вы собираете расширение для *конкретной* системы или только для себя, вы, возможно, захотите запустить компилятор напрямую. Способ сделать это зависит от системы; будьте готовы к проблемам, которые придётся решать самостоятельно.510511#### Linux512513В Linux пакет разработки Python может включать команду `python3-config`, которая выводит необходимые флаги компилятора. Если вы её используете, проверьте, что она соответствует интерпретатору CPython, который вы будете использовать для загрузки модуля. Затем начните с следующей команды:514515```sh516gcc --shared $(python3-config --cflags --ldflags) spammodule.c -o spam.so517```518519Это должно создать файл `spam.so`, который нужно поместить в каталог [`sys.path`](https://python-all.ru/3.16/library/sys.html#sys.path).520521Сноски522523\[[1](https://python-all.ru/3.16/extending/first-extension-module.html#id1)\]524525`spam` – любимая еда фанатов Монти Пайтона…526527\[[2](https://python-all.ru/3.16/extending/first-extension-module.html#id2)\]528529Имя исходного файла полностью на ваше усмотрение, хотя некоторые инструменты могут быть придирчивы к расширению `.c`. В этом руководстве используется традиционный суффикс `*module.c`. Некоторые используют `spam.c` для реализации модуля с именем `spam`, в проектах, где Python не является основным языком, могут использовать `py_spam.c`, и так далее.530531\[[3](https://python-all.ru/3.16/extending/first-extension-module.html#id3)\]532533Включать `stdlib.h` технически не обязательно, поскольку `Python.h` включает его и [несколько других стандартных заголовков](https://python-all.ru/3.16/c-api/intro.html#capi-system-includes) для собственных нужд или для обратной совместимости. Однако рекомендуется явно включать то, что нужно.534535\[[4](https://python-all.ru/3.16/extending/first-extension-module.html#id4)\]536537Структура `PyMethodDef` также используется для создания методов классов, поэтому отдельного «`PyFunctionDef`» не существует.538539\[[5](https://python-all.ru/3.16/extending/first-extension-module.html#id5)\]540541Название [`PyLong_FromLong()`](https://python-all.ru/3.16/c-api/long.html#c.PyLong_FromLong) может показаться неочевидным. `PyLong` относится к Python [`int`](https://python-all.ru/3.16/library/functions.html#int), который изначально назывался `long`; `FromLong` относится к типу C `long` (или `long int`).542543\[[6](https://python-all.ru/3.16/extending/first-extension-module.html#id6)\]544545Здесь `PyUnicode` относится к исходному названию класса Python [`str`](https://python-all.ru/3.16/library/stdtypes.html#str): `unicode`.546547Часть названия `AndSize` указывает на то, что эта функция может также получать размер буфера через выходной аргумент. Нам это не нужно, поэтому устанавливаем второй аргумент в NULL.548549\[[7](https://python-all.ru/3.16/extending/first-extension-module.html#id7)\]550551Мы игнорируем тот факт, что строки Python могут содержать нулевые байты (NUL), которые завершают C-строку. Другими словами, наша функция будет считать `spam.system("foo\0bar")` `spam.system("foo")`. Эта возможность может привести к проблемам безопасности, поэтому настоящая функция `os.system` проверяет размер для такого случая и вызывает исключение.552