0Pricing
Python Academy · Lekcja

Pisanie modułu rozszerzeń C dla Pythona

Proszę zbudować proste rozszerzenie .so za pomocą API Python/C.

Pisanie modułu rozszerzeń C dla Pythona to bezpłatna lekcja Python Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Python Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Python Academy zawiera 4 lekcji w sumie.

Budowa rozszerzenia C

Minimalne rozszerzenie C składa się z funkcji metod, tablicy metod, struktury definicji modułu oraz punktu wejścia PyInit_.

// myext.c skeleton
#include <Python.h>

static PyObject* say_hello(PyObject* self, PyObject* args) {
    Py_RETURN_NONE;
}

static PyMethodDef methods[] = {
    {"say_hello", say_hello, METH_NOARGS, "Print hello"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef module = {
    PyModuleDef_HEAD_INIT, "myext", NULL, -1, methods
};

PyMODINIT_FUNC PyInit_myext(void) {
    return PyModule_Create(&module);
}

Analizowanie argumentów

PyArg_ParseTuple(args, "ii", &a, &b) analizuje argumenty Pythona i zapisuje je w zmiennych C. Kody formatu: i=int, d=double, s=char*, O=PyObject*.

static PyObject* add(PyObject* self, PyObject* args) {
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b))
        return NULL;
    return PyLong_FromLong(a + b);
}

Zwracanie wartości

Wartości zwracane do Pythona należy tworzyć za pomocą: PyLong_FromLong, PyFloat_FromDouble, PyUnicode_FromString, Py_BuildValue.

// Return a Python tuple (int, float)
static PyObject* stats(PyObject* self, PyObject* args) {
    int n = 10;
    double avg = 5.0;
    return Py_BuildValue("(id)", n, avg);
    // Py_BuildValue format: i=int d=double s=str
}

Zliczanie referencji

Każdy obiekt PyObject* ma licznik referencji. Z makr Py_INCREF/Py_DECREF należy korzystać ostrożnie. Wartości zwracane przez funkcje C przekazują wywołującemu własność obiektu (stolen references).

static PyObject* make_list(PyObject* self, PyObject* args) {
    PyObject* lst = PyList_New(3);
    for (int i = 0; i < 3; i++) {
        // PyList_SET_ITEM steals the reference:
        PyList_SET_ITEM(lst, i, PyLong_FromLong(i));
    }
    return lst;   // caller owns the list
}

Zgłaszanie wyjątków

Proszę użyć PyErr_SetString(PyExc_ValueError, "msg"), aby zgłosić wyjątek Pythona z poziomu C, a następnie zwrócić NULL.

static PyObject* safe_div(PyObject* self, PyObject* args) {
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL;
    if (b == 0) {
        PyErr_SetString(PyExc_ZeroDivisionError, "division by zero");
        return NULL;
    }
    return PyLong_FromLong(a / b);
}

setup.py dla rozszerzeń C

Proszę użyć setuptools.Extension, aby zadeklarować pliki źródłowe C. Kompilację należy wykonać za pomocą python setup.py build_ext --inplace.

# setup.py
from setuptools import setup, Extension

setup(
    name="myext",
    ext_modules=[
        Extension(
            "myext",
            sources=["myext.c"],
            extra_compile_args=["-O2"],
        )
    ]
)
# python setup.py build_ext --inplace
# import myext; myext.add(3, 4)

Korzystanie ze zbudowanego rozszerzenia

Po zbudowaniu rozszerzenie można zaimportować jak dowolny moduł Pythona. Python wyszukuje je, szukając pliku myext.so lub .pyd w systemie Windows.

# After: python setup.py build_ext --inplace
import myext
print(myext.add(3, 4))        # 7
print(myext.safe_div(10, 2))  # 5
# myext.safe_div(1, 0)         # ZeroDivisionError

Argumenty nazwane

Proszę użyć PyArg_ParseTupleAndKeywords wraz z listą nazw argumentów, aby obsługiwać argumenty nazwane w funkcjach C.

static char* kwargs[] = {"x", "y", NULL};

static PyObject* hypot_c(PyObject* self,
                          PyObject* args,
                          PyObject* kw) {
    double x, y;
    if (!PyArg_ParseTupleAndKeywords(args, kw, "dd",
                                     kwargs, &x, &y))
        return NULL;
    return PyFloat_FromDouble(sqrt(x*x + y*y));
}

Stałe na poziomie modułu

Stałe całkowite lub tekstowe można dodać do modułu w PyInit_ za pomocą PyModule_AddIntConstant.

PyMODINIT_FUNC PyInit_myext(void) {
    PyObject* m = PyModule_Create(&module);
    if (!m) return NULL;
    PyModule_AddIntConstant(m, "VERSION", 1);
    PyModule_AddStringConstant(m, "AUTHOR", "Alice");
    return m;
}

Zwalnianie GIL

Proszę otoczyć operacje wykonywane wyłącznie w C za pomocą Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS, aby umożliwić działanie innym wątkom Pythona podczas wykonywania kodu C.

static PyObject* heavy(PyObject* self, PyObject* args) {
    long n;
    if (!PyArg_ParseTuple(args, "l", &n)) return NULL;
    long result;
    Py_BEGIN_ALLOW_THREADS
        result = slow_c_computation(n);  // GIL released
    Py_END_ALLOW_THREADS
    return PyLong_FromLong(result);
}

Testowanie rozszerzenia

Rozszerzenia C należy testować za pośrednictwem ich interfejsu Python przy użyciu pytest. Nie są potrzebne żadne specjalne narzędzia do testowania C.

# test_myext.py
import pytest
import myext

def test_add(): assert myext.add(3, 4) == 7
def test_zero_div():
    with pytest.raises(ZeroDivisionError):
        myext.safe_div(1, 0)

Szybkie sprawdzenie

Co musi zwrócić funkcja rozszerzenia C, aby zasygnalizować, że ustawiono wyjątek Pythona?

Podsumowanie

Rozszerzenie C wymaga: funkcji metod (analizujących argumenty i zwracających PyObject*), tablicy metod, struktury definicji modułu oraz PyInit_name(). Wyjątki należy zgłaszać przez ustawienie ich za pomocą PyErr_SetString i zwrócenie NULL. W przypadku operacji wykonywanych wyłącznie w C należy zwalniać GIL.

Często zadawane pytania

Czy lekcja „Pisanie modułu rozszerzeń C dla Pythona” jest bezpłatna?

Tak — pełny tekst „Pisanie modułu rozszerzeń C dla Pythona” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Python Academy, przejdź na CoddyKit PRO. Kurs Python Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Pisanie modułu rozszerzeń C dla Pythona”?

Proszę zbudować proste rozszerzenie .so za pomocą API Python/C. Ćwiczysz Python Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Python Academy?

Nie wymagamy żadnego doświadczenia. Python Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.

Ile czasu zajmuje lekcja „Pisanie modułu rozszerzeń C dla Pythona”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Python Academy?

Tak. Każda lekcja Python Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Po co rozszerzenia C? Zastosowania i kompromisy
  2. ctypes: wywoływanie bibliotek C z Pythona
  3. cffi: interfejs funkcji zewnętrznych C
  4. Pisanie modułu rozszerzeń C dla Pythona
← Powrót do Python Academy