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

---

# Портирование модулей расширения на 3.0

| автор: | Benjamin Peterson |
| --- | --- |

Аннотация

Хотя изменение C-API не входило в цели Python 3.0, многочисленные изменения на уровне Python сделали невозможным сохранение API версии 2.x нетронутым. Фактически, некоторые изменения, такие как унификация [`int()`](https://python-all.ru/3.0/library/functions.html#int) и `long()`, более очевидны на уровне C. Данный документ стремится описать несовместимости и способы их обхода.

## Условная компиляция

Самый простой способ компилировать только часть кода для 3.0 – проверить, что `PY_MAJOR_VERSION` больше или равно 3.

```c
#if PY_MAJOR_VERSION >= 3
#define IS_PY3K
#endif
```

Отсутствующие функции API можно заменить псевдонимами на их эквиваленты в условных блоках.

## Изменения в API объектов

Python 3.0 объединил некоторые типы со схожими функциями, чётко разделив другие.

### Унификация str/unicode

Тип [`str()`](https://python-all.ru/3.0/library/functions.html#str) в Python 3.0 (функции `PyString_*` в C) эквивалентен типу\\n`unicode()` (`PyUnicode_*`) в версии 2.x. Старый 8-битный строковый тип стал\\n[`bytes()`](https://python-all.ru/3.0/library/functions.html#bytes). Python 2.6 и новее предоставляют заголовочный файл совместимости,\\n`bytesobject.h`, который отображает имена `PyBytes` на имена `PyString`. Для наилучшей совместимости с 3.0,\\n`PyUnicode` следует использовать для текстовых данных, а\\n`PyBytes` – для двоичных. Также важно помнить, что\\n`PyBytes` и `PyUnicode` в 3.0 не взаимозаменяемы, в отличие от\\n`PyString` и `PyString` в 2.x. Следующий пример демонстрирует\\nлучшие практики в отношении `PyUnicode`, `PyString` и\\n`PyBytes`.

```c
#include "stdlib.h"
#include "Python.h"
#include "bytesobject.h"

/* пример текста */
static PyObject *
say_hello(PyObject *self, PyObject *args) {
    PyObject *name, *result;

    if (!PyArg_ParseTuple(args, "U:say_hello", &name))
        return NULL;

    result = PyUnicode_FromFormat("Hello, %S!", name);
    return result;
}

/* просто пересылка */
static char * do_encode(PyObject *);

/* пример с байтами */
static PyObject *
encode_object(PyObject *self, PyObject *args) {
    char *encoded;
    PyObject *result, *myobj;

    if (!PyArg_ParseTuple(args, "O:encode_object", &myobj))
        return NULL;

    encoded = do_encode(myobj);
    if (encoded == NULL)
        return NULL;
    result = PyBytes_FromString(encoded);
    free(encoded);
    return result;
}
```

### Унификация long/int

В Python 3.0 есть только один целочисленный тип. Он называется [`int()`](https://python-all.ru/3.0/library/functions.html#int) на\\nуровне Python, но на самом деле соответствует типу `long()` из 2.x. В\\nC-API функции `PyInt_*` заменены их соседями `PyLong_*`. Лучший\\nспособ действий – использовать функции `PyInt_*`, псевдонимы для\\nкоторых `PyLong_*` находятся в `intobject.h`. Абстрактные API `PyNumber_*`\\nтакже можно использовать в некоторых случаях.

```c
#include "Python.h"
#include "intobject.h"

static PyObject *
add_ints(PyObject *self, PyObject *args) {
    int one, two;
    PyObject *result;

    if (!PyArg_ParseTuple(args, "ii:add_ints", &one, &two))
        return NULL;

    return PyInt_FromLong(one + two);
}
```

## Инициализация и состояние модуля

Python 3.0 имеет переработанную систему инициализации модулей расширения. (См. PEP [**PEP 3121**](https://python-all.ru/3.0/howto/cporting.html).) Вместо хранения состояния модуля в глобальных переменных, его следует хранить в структуре, специфичной для интерпретатора. Создание модулей, правильно работающих как в 2.x, так и в 3.0, – непростая задача. Следующий простой пример демонстрирует, как это сделать.

```c
#include "Python.h"

struct module_state {
    PyObject *error;
};

#if PY_MAJOR_VERSION >= 3
#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
#else
#define GETSTATE(m) (&_state)
static struct module_state _state;
#endif

static PyObject *
error_out(PyObject *m) {
    struct module_state *st = GETSTATE(m);
    PyErr_SetString(st->error, "something bad happened");
    return NULL;
}

static PyMethodDef myextension_methods[] = {
    {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL},
    {NULL, NULL}
};

#if PY_MAJOR_VERSION >= 3

static int myextension_traverse(PyObject *m, visitproc visit, void *arg) {
    Py_VISIT(GETSTATE(m)->error);
    return 0;
}

static int myextension_clear(PyObject *m) {
    Py_CLEAR(GETSTATE(m)->error);
    return 0;
}

static struct PyModuleDef moduledef = {
        PyModuleDef_HEAD_INIT,
        "myextension",
        NULL,
        sizeof(struct module_state),
        myextension_methods,
        NULL,
        myextension_traverse,
        myextension_clear,
        NULL
};

#define INITERROR return NULL

PyObject *
PyInit_myextension(void)

#else
#define INITERROR return

void
initmyextension(void)
#endif
{
#if PY_MAJOR_VERSION >= 3
    PyObject *module = PyModule_Create(&moduledef);
#else
    PyObject *module = Py_InitModule("myextension", myextension_methods);
#endif

    if (module == NULL)
        INITERROR;
    struct module_state *st = GETSTATE(module);

    st->error = PyErr_NewException("myextension.Error", NULL, NULL);
    if (st->error == NULL) {
        Py_DECREF(module);
        INITERROR;
    }

#if PY_MAJOR_VERSION >= 3
    return module;
#endif
}
```

## Другие варианты

Если вы пишете новый модуль расширения, возможно, стоит рассмотреть [Cython](https://python-all.ru/3.0/howto/cporting.html). Он транслирует язык, похожий на Python, в C. Создаваемые им модули расширения совместимы с Python 3.x и 2.x.
