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

FAQ по расширению и встраиваниюExtending/Embedding FAQ

Содержание

Могу ли я создавать собственные функции на C?Can I create my own functions in C?

Да, можно создавать встроенные модули, содержащие функции, переменные, исключения и даже новые типы на C. Это описано в документе Extending and Embedding the Python Interpreter.

Эта тема также освещается в большинстве книг по Python для среднего и продвинутого уровня.

Могу ли я создавать собственные функции на C++?Can I create my own functions in C++?

Да, это возможно благодаря средствам совместимости с C в C++. Следует поместить extern "C" { ... } вокруг включаемых файлов Python и добавить extern "C" перед каждой функцией, которая будет вызываться интерпретатором Python. Глобальные или статические объекты C++ с конструкторами, вероятно, лучше не использовать.

Писать на C сложно; есть ли альтернативы?Writing C is hard; are there any alternatives?

Существует ряд альтернатив написанию собственных расширений на C – в зависимости от решаемой задачи.

Cython и его родственник Pyrex – это компиляторы, которые принимают слегка изменённую форму Python и генерируют соответствующий код на C. Cython и Pyrex позволяют писать расширения, не изучая C API Python.

Если требуется взаимодействие с библиотекой на C или C++, для которой в настоящее время нет расширения Python, можно попробовать обернуть типы данных и функции библиотеки с помощью такого инструмента, как SWIG. SIP, CXX, Boost или Weave также являются альтернативами для обёртывания библиотек C++.

Как выполнить произвольные операторы Python из C?How can I execute arbitrary Python statements from C?

Самая высокоуровневая функция для этого – PyRun_SimpleString(), которая принимает единственный строковый аргумент для выполнения в контексте модуля __main__ и возвращает 0 при успехе и -1, если возникло исключение (включая SyntaxError). Если нужен более тонкий контроль, используйте PyRun_String(); см. исходный код PyRun_SimpleString() в Python/pythonrun.c.

Как вычислить произвольное выражение Python из C?How can I evaluate an arbitrary Python expression from C?

Вызовите функцию PyRun_String() из предыдущего вопроса с начальным символом Py_eval_input; она разбирает выражение, вычисляет его и возвращает его значение.

Как извлечь значения C из объекта Python?How do I extract C values from a Python object?

Это зависит от типа объекта. Если это кортеж, PyTuple_Size() возвращает его длину, а PyTuple_GetItem() возвращает элемент по указанному индексу. У списков есть аналогичные функции: PyListSize() и PyList_GetItem().

Для bytes PyBytes_Size() возвращает его длину, а PyBytes_AsStringAndSize() предоставляет указатель на его значение и его длину. Обратите внимание, что объекты Python bytes могут содержать нулевые байты, поэтому C'шный strlen() использовать не следует.

Чтобы проверить тип объекта, сначала убедитесь, что он не равен NULL, а затем используйте PyBytes_Check(), PyTuple_Check(), PyList_Check() и т.д.

Существует также высокоуровневый API для объектов Python, предоставляемый так называемым «абстрактным» интерфейсом – читайте Include/abstract.h для получения дополнительных сведений. Он позволяет взаимодействовать с любыми последовательностями Python с помощью вызовов вроде PySequence_Length(), PySequence_GetItem() и т.д., а также многих других полезных протоколов, таких как числа (PyNumber_Index() и др.) и отображения в API PyMapping.

Как использовать Py_BuildValue() для создания кортежа произвольной длины?How do I use Py_BuildValue() to create a tuple of arbitrary length?

Никак. Вместо этого используйте PyTuple_Pack().

Как вызвать метод объекта из C?How do I call an object’s method from C?

Функцию PyObject_CallMethod() можно использовать для вызова произвольного метода объекта. Параметры: объект, имя вызываемого метода, строка форматирования, подобная используемой с Py_BuildValue(), и значения аргументов:

PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
                    const char *arg_format, ...);

Это работает для любого объекта, имеющего методы – как встроенные, так и определённые пользователем. Вы отвечаете за последующий Py_DECREF() возвращаемого значения.

Например, чтобы вызвать метод “seek” файлового объекта с аргументами 10, 0 (предполагая, что указатель на файловый объект – “f”):

res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
        ... an exception occurred ...
}
else {
        Py_DECREF(res);
}

Обратите внимание, что PyObject_CallObject() всегда ожидает кортеж в качестве списка аргументов; чтобы вызвать функцию без аргументов, передайте “()” в качестве формата, а чтобы вызвать функцию с одним аргументом, заключите аргумент в круглые скобки, например “(i)”.

Как перехватить вывод PyErr_Print() (или всего, что печатает в stdout/stderr)?How do I catch the output from PyErr_Print() (or anything that prints to stdout/stderr)?

В коде Python определите объект, поддерживающий метод write(). Назначьте этот объект на sys.stdout и sys.stderr. Вызовите print_error или просто позвольте стандартному механизму трассировки сработать. Тогда вывод будет направлен туда, куда его отправляет метод write().

Проще всего это сделать с помощью класса io.StringIO:

>>> import io, sys
>>> sys.stdout = io.StringIO()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
hello world!

Пользовательский объект для того же самого мог бы выглядеть так:

>>> import io, sys
>>> class StdoutCatcher(io.TextIOBase):
...     def __init__(self):
...         self.data = []
...     def write(self, stuff):
...         self.data.append(stuff)
...
>>> import sys
>>> sys.stdout = StdoutCatcher()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(''.join(sys.stdout.data))
foo
hello world!

Как получить доступ к модулю, написанному на Python, из C?How do I access a module written in Python from C?

Указатель на объект модуля можно получить следующим образом:

module = PyImport_ImportModule("<modulename>");

Если модуль ещё не был импортирован (т.е. его ещё нет в sys.modules), то выполняется его инициализация; в противном случае просто возвращается значение sys.modules["<modulename>"]. Обратите внимание: модуль не добавляется ни в одно пространство имён – гарантируется только его инициализация и сохранение в sys.modules.

Затем можно обратиться к атрибутам модуля (т.е. к любым именам, определённым в модуле) следующим образом:

attr = PyObject_GetAttrString(module, "<attrname>");

Вызов PyObject_SetAttrString() для присваивания переменным в модуле также работает.

Как организовать взаимодействие с объектами C++ из Python?How do I interface to C++ objects from Python?

В зависимости от требований существует множество подходов. Для ручного выполнения начните с чтения документа «Extending and Embedding». Следует понимать, что для системы времени выполнения Python нет большой разницы между C и C++ – поэтому стратегия создания нового типа Python на основе C-структуры (указателя) будет работать и для объектов C++.

Для библиотек C++ см. Писать на C сложно; есть ли альтернативы?.

Я добавил модуль через файл Setup, но make завершается ошибкой; почему?I added a module using the Setup file and the make fails; why?

Файл Setup должен заканчиваться символом новой строки; если его нет, сборка завершается ошибкой. (Исправление требует уродливых ухищрений в shell-скриптах, и этот баг настолько незначителен, что не стоит затраченных усилий.)

Как отлаживать расширение?How do I debug an extension?

При использовании GDB с динамически загружаемыми расширениями невозможно установить точку останова в расширении до его загрузки.

В файле .gdbinit (или интерактивно) добавьте команду:

br _PyImport_LoadDynamicModule

Затем при запуске GDB:

$ gdb /local/bin/python
gdb) run myscript.py
gdb) continue # repeat until your extension is loaded
gdb) finish   # so that your extension is loaded
gdb) br myfunction.c:50
gdb) continue

Хочу скомпилировать модуль Python в своей системе Linux, но не хватает некоторых файлов. Почему?I want to compile a Python module on my Linux system, but some files are missing. Why?

Большинство собранных версий Python не включают каталог /usr/lib/python2.x/config/, который содержит различные файлы необходимые для компиляции расширений Python.

Для Red Hat установите пакет python-devel RPM, чтобы получить необходимые файлы.

Для Debian выполните apt-get install python-dev.

Как отличить «неполный ввод» от «недопустимого ввода»?How do I tell “incomplete input” from “invalid input”?

Иногда требуется эмулировать поведение интерактивного интерпретатора Python, где выдаётся приглашение продолжения ввода, когда ввод неполон (например, напечатано начало инструкции «if» или не закрыты скобки или тройные кавычки), но при недопустимом вводе сразу выдаётся сообщение об ошибке синтаксиса.

В Python можно использовать модуль codeop, который достаточно точно воспроизводит поведение парсера. Например, так делает IDLE.

Самый простой способ сделать это в C – вызвать PyRun_InteractiveLoop() (возможно, в отдельном потоке) и позволить интерпретатору Python обработать ввод за вас. Также можно установить PyOS_ReadlineFunctionPointer() так, чтобы он указывал на вашу пользовательскую функцию ввода. Дополнительные подсказки см. в Modules/readline.c и Parser/myreadline.c.

Однако иногда требуется запускать встроенный интерпретатор Python в том же потоке, что и основное приложение, и нельзя позволить PyRun_InteractiveLoop() останавливаться в ожидании ввода пользователя. Тогда одно из решений – вызвать PyParser_ParseString() и проверить, равен ли e.error E_EOF (что означает неполный ввод). Вот пример фрагмента кода, не тестировавшийся, основанный на коде Алекса Фарбера:

#include <Python.h>
#include <node.h>
#include <errcode.h>
#include <grammar.h>
#include <parsetok.h>
#include <compile.h>

int testcomplete(char *code)
  /* код должен заканчиваться на \n */
  /* возвращает -1 при ошибке, 0 при неполном коде, 1 при завершённом */
{
  node *n;
  perrdetail e;

  n = PyParser_ParseString(code, &_PyParser_Grammar,
                           Py_file_input, &e);
  if (n == NULL) {
    if (e.error == E_EOF)
      return 0;
    return -1;
  }

  PyNode_Free(n);
  return 1;
}

Другое решение – попробовать скомпилировать полученную строку с помощью Py_CompileString(). Если она компилируется без ошибок, выполнить возвращённый объект кода вызовом PyEval_EvalCode(). В противном случае сохранить ввод для последующей обработки. Если компиляция не удалась, выяснить, является ли это ошибкой или требуется ещё ввод, извлекая строку сообщения из кортежа исключения и сравнивая её со строкой «unexpected EOF while parsing». Вот полный пример с использованием библиотеки GNU readline (возможно, стоит игнорировать SIGINT при вызове readline()):

#include <stdio.h>
#include <readline.h>

#include <Python.h>
#include <object.h>
#include <compile.h>
#include <eval.h>

int main (int argc, char* argv[])
{
  int i, j, done = 0;                          /* длины строки и кода */
  char ps1[] = ">>> ";
  char ps2[] = "... ";
  char *prompt = ps1;
  char *msg, *line, *code = NULL;
  PyObject *src, *glb, *loc;
  PyObject *exc, *val, *trb, *obj, *dum;

  Py_Initialize ();
  loc = PyDict_New ();
  glb = PyDict_New ();
  PyDict_SetItemString (glb, "__builtins__", PyEval_GetBuiltins ());

  while (!done)
  {
    line = readline (prompt);

    if (NULL == line)                          /* нажата Ctrl-D */
    {
      done = 1;
    }
    else
    {
      i = strlen (line);

      if (i > 0)
        add_history (line);                    /* сохранять непустые строки */

      if (NULL == code)                        /* в коде пока ничего нет */
        j = 0;
      else
        j = strlen (code);

      code = realloc (code, i + j + 2);
      if (NULL == code)                        /* недостаточно памяти */
        exit (1);

      if (0 == j)                              /* код был пуст, поэтому */
        code[0] = '\0';                        /* чтобы strncat работал корректно */

      strncat (code, line, i);                 /* добавить строку к коду */
      code[i + j] = '\n';                      /* добавить '\n' к коду */
      code[i + j + 1] = '\0';

      src = Py_CompileString (code, "<stdin>", Py_single_input);

      if (NULL != src)                         /* скомпилировано успешно - */
      {
        if (ps1  == prompt ||                  /* ">>> " или */
            '\n' == code[i + j - 1])           /* "... " и двойной '\n' */
        {                                               /* поэтому выполнить его */
          dum = PyEval_EvalCode (src, glb, loc);
          Py_XDECREF (dum);
          Py_XDECREF (src);
          free (code);
          code = NULL;
          if (PyErr_Occurred ())
            PyErr_Print ();
          prompt = ps1;
        }
      }                                        /* синтаксическая ошибка или E_EOF? */
      else if (PyErr_ExceptionMatches (PyExc_SyntaxError))
      {
        PyErr_Fetch (&exc, &val, &trb);        /* сбрасывает исключение! */

        if (PyArg_ParseTuple (val, "sO", &msg, &obj) &&
            !strcmp (msg, "unexpected EOF while parsing")) /* E_EOF */
        {
          Py_XDECREF (exc);
          Py_XDECREF (val);
          Py_XDECREF (trb);
          prompt = ps2;
        }
        else                                   /* какая-то другая синтаксическая ошибка */
        {
          PyErr_Restore (exc, val, trb);
          PyErr_Print ();
          free (code);
          code = NULL;
          prompt = ps1;
        }
      }
      else                                     /* какая-то не-синтаксическая ошибка */
      {
        PyErr_Print ();
        free (code);
        code = NULL;
        prompt = ps1;
      }

      free (line);
    }
  }

  Py_XDECREF(glb);
  Py_XDECREF(loc);
  Py_Finalize();
  exit(0);
}

Как найти неопределённые символы g++ __builtin_new или __pure_virtual?How do I find undefined g++ symbols __builtin_new or __pure_virtual?

Для динамической загрузки модулей расширения g++ необходимо перекомпилировать Python, перекомпоновать его с помощью g++ (измените LINKCC в Makefile модулей Python) и скомпоновать модуль расширения с помощью g++ (например, g++ -shared -o mymodule.so mymodule.o).

Можно ли создать класс объектов, в котором одни методы реализованы на C, а другие – на Python (например, через наследование)?Can I create an object class with some methods implemented in C and others in Python (e.g. through inheritance)?

Да, можно наследовать от встроенных классов, таких как int, list, dict и т.д.

Библиотека Boost.Python (BPL, http://www.boost.org/libs/python/doc/index.html) предоставляет способ сделать это из C++ (т.е. вы можете наследоваться от класса расширения, написанного на C++, с помощью BPL).