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

extending.md

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

1> **Источник:** https://python-all.ru/3.5/faq/extending.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# [FAQ по расширению и встраиванию](https://python-all.ru/3.5/faq/extending.html#id2)89Содержание1011- [FAQ по расширению и встраиванию](https://python-all.ru/3.5/faq/extending.html#extending-embedding-faq)1213  - [Можно ли создавать собственные функции на C?](https://python-all.ru/3.5/faq/extending.html#can-i-create-my-own-functions-in-c)14  - [Можно ли создавать собственные функции на C++?](https://python-all.ru/3.5/faq/extending.html#id1)15  - [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/3.5/faq/extending.html#writing-c-is-hard-are-there-any-alternatives)16  - [Как выполнить произвольные инструкции Python из C?](https://python-all.ru/3.5/faq/extending.html#how-can-i-execute-arbitrary-python-statements-from-c)17  - [Как вычислить произвольное выражение Python из C?](https://python-all.ru/3.5/faq/extending.html#how-can-i-evaluate-an-arbitrary-python-expression-from-c)18  - [Как извлечь значения C из объекта Python?](https://python-all.ru/3.5/faq/extending.html#how-do-i-extract-c-values-from-a-python-object)19  - [Как использовать Py\_BuildValue() для создания кортежа произвольной длины?](https://python-all.ru/3.5/faq/extending.html#how-do-i-use-py-buildvalue-to-create-a-tuple-of-arbitrary-length)20  - [Как вызвать метод объекта из C?](https://python-all.ru/3.5/faq/extending.html#how-do-i-call-an-object-s-method-from-c)21  - [Как перехватить вывод PyErr\_Print() (или любого другого вывода в stdout/stderr)?](https://python-all.ru/3.5/faq/extending.html#how-do-i-catch-the-output-from-pyerr-print-or-anything-that-prints-to-stdout-stderr)22  - [Как получить доступ к модулю, написанному на Python, из C?](https://python-all.ru/3.5/faq/extending.html#how-do-i-access-a-module-written-in-python-from-c)23  - [Как организовать взаимодействие с объектами C++ из Python?](https://python-all.ru/3.5/faq/extending.html#how-do-i-interface-to-c-objects-from-python)24  - [Почему make завершается ошибкой после добавления модуля через файл Setup?](https://python-all.ru/3.5/faq/extending.html#i-added-a-module-using-the-setup-file-and-the-make-fails-why)25  - [Как отлаживать расширение?](https://python-all.ru/3.5/faq/extending.html#how-do-i-debug-an-extension)26  - [Почему при компиляции модуля Python в Linux отсутствуют некоторые файлы?](https://python-all.ru/3.5/faq/extending.html#i-want-to-compile-a-python-module-on-my-linux-system-but-some-files-are-missing-why)27  - [Как отличить «неполный ввод» от «неверного ввода»?](https://python-all.ru/3.5/faq/extending.html#how-do-i-tell-incomplete-input-from-invalid-input)28  - [Как найти неопределённые символы g++ \_\_builtin\_new или \_\_pure\_virtual?](https://python-all.ru/3.5/faq/extending.html#how-do-i-find-undefined-g-symbols-builtin-new-or-pure-virtual)29  - [Можно ли создать класс объекта, в котором часть методов реализована на C, а часть – на Python (например, через наследование)?](https://python-all.ru/3.5/faq/extending.html#can-i-create-an-object-class-with-some-methods-implemented-in-c-and-others-in-python-e-g-through-inheritance)3031## [Могу ли я создавать собственные функции на C?](https://python-all.ru/3.5/faq/extending.html#id3)3233Да, можно создавать встроенные модули, содержащие функции, переменные, исключения и даже новые типы на C. Это описано в документе [Extending and Embedding the Python Interpreter](https://python-all.ru/3.5/extending/index.html#extending-index).3435Эта тема также освещается в большинстве книг по Python для среднего и продвинутого уровня.3637## [Могу ли я создавать собственные функции на C++?](https://python-all.ru/3.5/faq/extending.html#id4)3839Да, это возможно благодаря средствам совместимости с C в C++. Следует поместить `extern "C" { ... }` вокруг включаемых файлов Python и добавить `extern "C"` перед каждой функцией, которая будет вызываться интерпретатором Python. Глобальные или статические объекты C++ с конструкторами, вероятно, лучше не использовать.4041## [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/3.5/faq/extending.html#id5)4243Существует ряд альтернатив написанию собственных расширений на C – в зависимости от решаемой задачи.4445[Cython](https://python-all.ru/3.5/faq/extending.html) и его родственник [Pyrex](https://python-all.ru/3.5/faq/extending.html) – это компиляторы, которые принимают слегка изменённую форму Python и генерируют соответствующий код на C. Cython и Pyrex позволяют писать расширения, не изучая C API Python.4647Если требуется взаимодействие с библиотекой на C или C++, для которой в настоящее время нет расширения Python, можно попробовать обернуть типы данных и функции библиотеки с помощью такого инструмента, как [SWIG](https://python-all.ru/3.5/faq/extending.html). [SIP](https://python-all.ru/3.5/faq/extending.html), [CXX](https://python-all.ru/3.5/faq/extending.html), [Boost](https://python-all.ru/3.5/faq/extending.html) или [Weave](https://python-all.ru/3.5/faq/extending.html) также являются альтернативами для обёртывания библиотек C++.4849## [Как выполнить произвольные операторы Python из C?](https://python-all.ru/3.5/faq/extending.html#id6)5051Самая высокоуровневая функция для этого – [`PyRun_SimpleString()`](https://python-all.ru/3.5/c-api/veryhigh.html#c.PyRun_SimpleString), которая принимает единственный строковый аргумент для выполнения в контексте модуля `__main__` и возвращает 0 при успехе и -1, если возникло исключение (включая `SyntaxError`). Если нужен более тонкий контроль, используйте [`PyRun_String()`](https://python-all.ru/3.5/c-api/veryhigh.html#c.PyRun_String); см. исходный код [`PyRun_SimpleString()`](https://python-all.ru/3.5/c-api/veryhigh.html#c.PyRun_SimpleString) в `Python/pythonrun.c`.5253## [Как вычислить произвольное выражение Python из C?](https://python-all.ru/3.5/faq/extending.html#id7)5455Вызовите функцию [`PyRun_String()`](https://python-all.ru/3.5/c-api/veryhigh.html#c.PyRun_String) из предыдущего вопроса с начальным символом [`Py_eval_input`](https://python-all.ru/3.5/c-api/veryhigh.html#c.Py_eval_input); она разбирает выражение, вычисляет его и возвращает его значение.5657## [Как извлечь значения C из объекта Python?](https://python-all.ru/3.5/faq/extending.html#id8)5859Это зависит от типа объекта. Если это кортеж, [`PyTuple_Size()`](https://python-all.ru/3.5/c-api/tuple.html#c.PyTuple_Size) возвращает его длину, а [`PyTuple_GetItem()`](https://python-all.ru/3.5/c-api/tuple.html#c.PyTuple_GetItem) возвращает элемент по указанному индексу. У списков есть аналогичные функции: `PyListSize()` и [`PyList_GetItem()`](https://python-all.ru/3.5/c-api/list.html#c.PyList_GetItem).6061Для bytes [`PyBytes_Size()`](https://python-all.ru/3.5/c-api/bytes.html#c.PyBytes_Size) возвращает его длину, а [`PyBytes_AsStringAndSize()`](https://python-all.ru/3.5/c-api/bytes.html#c.PyBytes_AsStringAndSize) предоставляет указатель на его значение и его длину. Обратите внимание, что объекты Python bytes могут содержать нулевые байты, поэтому C'шный `strlen()` использовать не следует.6263Чтобы проверить тип объекта, сначала убедитесь, что он не равен *NULL*, а затем используйте [`PyBytes_Check()`](https://python-all.ru/3.5/c-api/bytes.html#c.PyBytes_Check), [`PyTuple_Check()`](https://python-all.ru/3.5/c-api/tuple.html#c.PyTuple_Check), [`PyList_Check()`](https://python-all.ru/3.5/c-api/list.html#c.PyList_Check) и т.д.6465Существует также высокоуровневый API для объектов Python, предоставляемый так называемым «абстрактным» интерфейсом – читайте `Include/abstract.h` для получения дополнительных сведений. Он позволяет взаимодействовать с любыми последовательностями Python с помощью вызовов вроде [`PySequence_Length()`](https://python-all.ru/3.5/c-api/sequence.html#c.PySequence_Length), [`PySequence_GetItem()`](https://python-all.ru/3.5/c-api/sequence.html#c.PySequence_GetItem) и т.д., а также многих других полезных протоколов, таких как числа ([`PyNumber_Index()`](https://python-all.ru/3.5/c-api/number.html#c.PyNumber_Index) и др.) и отображения в API PyMapping.6667## [Как использовать Py\_BuildValue() для создания кортежа произвольной длины?](https://python-all.ru/3.5/faq/extending.html#id9)6869Никак. Вместо этого используйте [`PyTuple_Pack()`](https://python-all.ru/3.5/c-api/tuple.html#c.PyTuple_Pack).7071## [Как вызвать метод объекта из C?](https://python-all.ru/3.5/faq/extending.html#id10)7273Функцию [`PyObject_CallMethod()`](https://python-all.ru/3.5/c-api/object.html#c.PyObject_CallMethod) можно использовать для вызова произвольного метода объекта. Параметры: объект, имя вызываемого метода, строка форматирования, подобная используемой с [`Py_BuildValue()`](https://python-all.ru/3.5/c-api/arg.html#c.Py_BuildValue), и значения аргументов:7475```c76PyObject *77PyObject_CallMethod(PyObject *object, const char *method_name,78                    const char *arg_format, ...);79```8081Это работает для любого объекта, имеющего методы – как встроенные, так и определённые пользователем. Вы отвечаете за последующий [`Py_DECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_DECREF) возвращаемого значения.8283Например, чтобы вызвать метод “seek” файлового объекта с аргументами 10, 0 (предполагая, что указатель на файловый объект – “f”):8485```c86res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);87if (res == NULL) {88        ... an exception occurred ...89}90else {91        Py_DECREF(res);92}93```9495Обратите внимание, что [`PyObject_CallObject()`](https://python-all.ru/3.5/c-api/object.html#c.PyObject_CallObject) *всегда* ожидает кортеж в качестве списка аргументов; чтобы вызвать функцию без аргументов, передайте “()” в качестве формата, а чтобы вызвать функцию с одним аргументом, заключите аргумент в круглые скобки, например “(i)”.9697## [Как перехватить вывод PyErr\_Print() (или всего, что печатает в stdout/stderr)?](https://python-all.ru/3.5/faq/extending.html#id11)9899В коде Python определите объект, поддерживающий метод `write()`. Назначьте этот объект на [`sys.stdout`](https://python-all.ru/3.5/library/sys.html#sys.stdout) и [`sys.stderr`](https://python-all.ru/3.5/library/sys.html#sys.stderr). Вызовите print\_error или просто позвольте стандартному механизму трассировки сработать. Тогда вывод будет направлен туда, куда его отправляет метод `write()`.100101Проще всего это сделать с помощью класса [`io.StringIO`](https://python-all.ru/3.5/library/io.html#io.StringIO):102103```pycon104>>> import io, sys105>>> sys.stdout = io.StringIO()106>>> print('foo')107>>> print('hello world!')108>>> sys.stderr.write(sys.stdout.getvalue())109foo110hello world!111```112113Пользовательский объект для того же самого мог бы выглядеть так:114115```pycon116>>> import io, sys117>>> class StdoutCatcher(io.TextIOBase):118...     def __init__(self):119...         self.data = []120...     def write(self, stuff):121...         self.data.append(stuff)122...123>>> import sys124>>> sys.stdout = StdoutCatcher()125>>> print('foo')126>>> print('hello world!')127>>> sys.stderr.write(''.join(sys.stdout.data))128foo129hello world!130```131132## [Как получить доступ к модулю, написанному на Python, из C?](https://python-all.ru/3.5/faq/extending.html#id12)133134Указатель на объект модуля можно получить следующим образом:135136```c137module = PyImport_ImportModule("<modulename>");138```139140Если модуль ещё не был импортирован (т.е. его ещё нет в [`sys.modules`](https://python-all.ru/3.5/library/sys.html#sys.modules)), то выполняется его инициализация; в противном случае просто возвращается значение `sys.modules["<modulename>"]`. Обратите внимание: модуль не добавляется ни в одно пространство имён – гарантируется только его инициализация и сохранение в [`sys.modules`](https://python-all.ru/3.5/library/sys.html#sys.modules).141142Затем можно обратиться к атрибутам модуля (т.е. к любым именам, определённым в модуле) следующим образом:143144```c145attr = PyObject_GetAttrString(module, "<attrname>");146```147148Вызов [`PyObject_SetAttrString()`](https://python-all.ru/3.5/c-api/object.html#c.PyObject_SetAttrString) для присваивания переменным в модуле также работает.149150## [Как организовать взаимодействие с объектами C++ из Python?](https://python-all.ru/3.5/faq/extending.html#id13)151152В зависимости от требований существует множество подходов. Для ручного выполнения начните с чтения [документа «Extending and Embedding»](https://python-all.ru/3.5/extending/index.html#extending-index). Следует понимать, что для системы времени выполнения Python нет большой разницы между C и C++ – поэтому стратегия создания нового типа Python на основе C-структуры (указателя) будет работать и для объектов C++.153154Для библиотек C++ см. [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/3.5/faq/extending.html#c-wrapper-software).155156## [Я добавил модуль через файл Setup, но make завершается ошибкой; почему?](https://python-all.ru/3.5/faq/extending.html#id14)157158Файл Setup должен заканчиваться символом новой строки; если его нет, сборка завершается ошибкой. (Исправление требует уродливых ухищрений в shell-скриптах, и этот баг настолько незначителен, что не стоит затраченных усилий.)159160## [Как отлаживать расширение?](https://python-all.ru/3.5/faq/extending.html#id15)161162При использовании GDB с динамически загружаемыми расширениями невозможно установить точку останова в расширении до его загрузки.163164В файле `.gdbinit` (или интерактивно) добавьте команду:165166```text167br _PyImport_LoadDynamicModule168```169170Затем при запуске GDB:171172```console173$ gdb /local/bin/python174gdb) run myscript.py175gdb) continue # repeat until your extension is loaded176gdb) finish   # so that your extension is loaded177gdb) br myfunction.c:50178gdb) continue179```180181## [Хочу скомпилировать модуль Python в своей системе Linux, но не хватает некоторых файлов. Почему?](https://python-all.ru/3.5/faq/extending.html#id16)182183Большинство собранных версий Python не включают каталог `/usr/lib/python2.x/config/`, который содержит различные файлы необходимые для компиляции расширений Python.184185Для Red Hat установите пакет python-devel RPM, чтобы получить необходимые файлы.186187Для Debian выполните `apt-get install python-dev`.188189## [Как отличить «неполный ввод» от «недопустимого ввода»?](https://python-all.ru/3.5/faq/extending.html#id17)190191Иногда требуется эмулировать поведение интерактивного интерпретатора Python, где выдаётся приглашение продолжения ввода, когда ввод неполон (например, напечатано начало инструкции «if» или не закрыты скобки или тройные кавычки), но при недопустимом вводе сразу выдаётся сообщение об ошибке синтаксиса.192193В Python можно использовать модуль [`codeop`](https://python-all.ru/3.5/library/codeop.html#module-codeop), который достаточно точно воспроизводит поведение парсера. Например, так делает IDLE.194195Самый простой способ сделать это в C – вызвать [`PyRun_InteractiveLoop()`](https://python-all.ru/3.5/c-api/veryhigh.html#c.PyRun_InteractiveLoop) (возможно, в отдельном потоке) и позволить интерпретатору Python обработать ввод за вас. Также можно установить [`PyOS_ReadlineFunctionPointer()`](https://python-all.ru/3.5/c-api/veryhigh.html#c.PyOS_ReadlineFunctionPointer) так, чтобы он указывал на вашу пользовательскую функцию ввода. Дополнительные подсказки см. в `Modules/readline.c` и `Parser/myreadline.c`.196197Однако иногда требуется запускать встроенный интерпретатор Python в том же потоке, что и основное приложение, и нельзя позволить [`PyRun_InteractiveLoop()`](https://python-all.ru/3.5/c-api/veryhigh.html#c.PyRun_InteractiveLoop) останавливаться в ожидании ввода пользователя. Тогда одно из решений – вызвать `PyParser_ParseString()` и проверить, равен ли `e.error` `E_EOF` (что означает неполный ввод). Вот пример фрагмента кода, не тестировавшийся, основанный на коде Алекса Фарбера:198199```c200#include <Python.h>201#include <node.h>202#include <errcode.h>203#include <grammar.h>204#include <parsetok.h>205#include <compile.h>206207int testcomplete(char *code)208  /* код должен заканчиваться на \n */209  /* возвращает -1 при ошибке, 0 при неполном коде, 1 при завершённом */210{211  node *n;212  perrdetail e;213214  n = PyParser_ParseString(code, &_PyParser_Grammar,215                           Py_file_input, &e);216  if (n == NULL) {217    if (e.error == E_EOF)218      return 0;219    return -1;220  }221222  PyNode_Free(n);223  return 1;224}225```226227Другое решение – попробовать скомпилировать полученную строку с помощью [`Py_CompileString()`](https://python-all.ru/3.5/c-api/veryhigh.html#c.Py_CompileString). Если она компилируется без ошибок, выполнить возвращённый объект кода вызовом [`PyEval_EvalCode()`](https://python-all.ru/3.5/c-api/veryhigh.html#c.PyEval_EvalCode). В противном случае сохранить ввод для последующей обработки. Если компиляция не удалась, выяснить, является ли это ошибкой или требуется ещё ввод, извлекая строку сообщения из кортежа исключения и сравнивая её со строкой «unexpected EOF while parsing». Вот полный пример с использованием библиотеки GNU readline (возможно, стоит игнорировать **SIGINT** при вызове readline()):228229```c230#include <stdio.h>231#include <readline.h>232233#include <Python.h>234#include <object.h>235#include <compile.h>236#include <eval.h>237238int main (int argc, char* argv[])239{240  int i, j, done = 0;                          /* длины строки и кода */241  char ps1[] = ">>> ";242  char ps2[] = "... ";243  char *prompt = ps1;244  char *msg, *line, *code = NULL;245  PyObject *src, *glb, *loc;246  PyObject *exc, *val, *trb, *obj, *dum;247248  Py_Initialize ();249  loc = PyDict_New ();250  glb = PyDict_New ();251  PyDict_SetItemString (glb, "__builtins__", PyEval_GetBuiltins ());252253  while (!done)254  {255    line = readline (prompt);256257    if (NULL == line)                          /* нажата Ctrl-D */258    {259      done = 1;260    }261    else262    {263      i = strlen (line);264265      if (i > 0)266        add_history (line);                    /* сохранять непустые строки */267268      if (NULL == code)                        /* в коде пока ничего нет */269        j = 0;270      else271        j = strlen (code);272273      code = realloc (code, i + j + 2);274      if (NULL == code)                        /* недостаточно памяти */275        exit (1);276277      if (0 == j)                              /* код был пуст, поэтому */278        code[0] = '\0';                        /* чтобы strncat работал корректно */279280      strncat (code, line, i);                 /* добавить строку к коду */281      code[i + j] = '\n';                      /* добавить '\n' к коду */282      code[i + j + 1] = '\0';283284      src = Py_CompileString (code, "<stdin>", Py_single_input);285286      if (NULL != src)                         /* скомпилировано успешно - */287      {288        if (ps1  == prompt ||                  /* ">>> " или */289            '\n' == code[i + j - 1])           /* "... " и двойной '\n' */290        {                                               /* поэтому выполнить его */291          dum = PyEval_EvalCode (src, glb, loc);292          Py_XDECREF (dum);293          Py_XDECREF (src);294          free (code);295          code = NULL;296          if (PyErr_Occurred ())297            PyErr_Print ();298          prompt = ps1;299        }300      }                                        /* синтаксическая ошибка или E_EOF? */301      else if (PyErr_ExceptionMatches (PyExc_SyntaxError))302      {303        PyErr_Fetch (&exc, &val, &trb);        /* сбрасывает исключение! */304305        if (PyArg_ParseTuple (val, "sO", &msg, &obj) &&306            !strcmp (msg, "unexpected EOF while parsing")) /* E_EOF */307        {308          Py_XDECREF (exc);309          Py_XDECREF (val);310          Py_XDECREF (trb);311          prompt = ps2;312        }313        else                                   /* какая-то другая синтаксическая ошибка */314        {315          PyErr_Restore (exc, val, trb);316          PyErr_Print ();317          free (code);318          code = NULL;319          prompt = ps1;320        }321      }322      else                                     /* какая-то не-синтаксическая ошибка */323      {324        PyErr_Print ();325        free (code);326        code = NULL;327        prompt = ps1;328      }329330      free (line);331    }332  }333334  Py_XDECREF(glb);335  Py_XDECREF(loc);336  Py_Finalize();337  exit(0);338}339```340341## [Как найти неопределённые символы g++ \_\_builtin\_new или \_\_pure\_virtual?](https://python-all.ru/3.5/faq/extending.html#id18)342343Для динамической загрузки модулей расширения g++ необходимо перекомпилировать Python, перекомпоновать его с помощью g++ (измените LINKCC в Makefile модулей Python) и скомпоновать модуль расширения с помощью g++ (например, `g++ -shared -o mymodule.so mymodule.o`).344345## [Можно ли создать класс объектов, в котором одни методы реализованы на C, а другие – на Python (например, через наследование)?](https://python-all.ru/3.5/faq/extending.html#id19)346347Да, можно наследовать от встроенных классов, таких как [`int`](https://python-all.ru/3.5/library/functions.html#int), [`list`](https://python-all.ru/3.5/library/stdtypes.html#list), [`dict`](https://python-all.ru/3.5/library/stdtypes.html#dict) и т.д.348349Библиотека Boost.Python (BPL, [http://www.boost.org/libs/python/doc/index.html](https://python-all.ru/3.5/faq/extending.html)) предоставляет способ сделать это из C++ (т.е. вы можете наследоваться от класса расширения, написанного на C++, с помощью BPL).350