0Pricing
Python Academy · บทเรียน

การเขียนโมดูลส่วนขยาย C ของ Python

สร้างส่วนขยาย .so อย่างง่ายด้วย Python/C API

การเขียนโมดูลส่วนขยาย C ของ Python เป็นบทเรียน Python Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Python Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Python Academy มีบทเรียนทั้งหมด 4 บทเรียน

โครงสร้างของส่วนขยาย C

ส่วนขยาย C ขั้นต่ำประกอบด้วย: ฟังก์ชันเมธอด ตารางเมธอด โครงสร้างนิยามโมดูล และจุดเข้าสู่ระบบ 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);
}

การแยกวิเคราะห์อาร์กิวเมนต์

PyArg_ParseTuple(args, "ii", &a, &b) แยกวิเคราะห์อาร์กิวเมนต์ของ Python ให้เป็นตัวแปร C รหัสรูปแบบมีดังนี้: 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);
}

การส่งคืนค่า

สร้างค่าที่ส่งคืนให้ Python ด้วย: 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
}

การนับจำนวนการอ้างอิง

PyObject* ทุกตัวมีจำนวนการอ้างอิง โปรดใช้ Py_INCREF/Py_DECREF อย่างระมัดระวัง ค่าที่ส่งคืนจากฟังก์ชัน C จะมอบสิทธิ์ความเป็นเจ้าของให้ผู้เรียก (การอ้างอิงที่ถูกโอนไป)

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
}

การโยนข้อยกเว้น

ใช้ PyErr_SetString(PyExc_ValueError, "msg") เพื่อโยนข้อยกเว้นของ Python จาก C จากนั้นส่งคืน 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 สำหรับส่วนขยาย C

ใช้ setuptools.Extension เพื่อประกาศไฟล์ซอร์ส C สร้างด้วย 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)

การใช้ส่วนขยายที่สร้างแล้ว

เมื่อสร้างเสร็จแล้ว ให้นำเข้าส่วนขยายเช่นเดียวกับโมดูล Python ใด ๆ Python จะค้นหาส่วนขยายโดยมองหา myext.so (หรือ .pyd บน 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

อาร์กิวเมนต์แบบคีย์เวิร์ด

ใช้ PyArg_ParseTupleAndKeywords ร่วมกับรายการคีย์เวิร์ด เพื่อรองรับอาร์กิวเมนต์แบบคีย์เวิร์ดในฟังก์ชัน 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));
}

ค่าคงที่ระดับโมดูล

เพิ่มค่าคงที่ชนิดจำนวนเต็มหรือสตริงลงในโมดูลภายใน PyInit_ โดยใช้ 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;
}

การปล่อย GIL

ครอบงาน C ล้วนด้วย Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS เพื่อให้เธรด Python อื่นทำงานได้ขณะที่โค้ด 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);
}

การทดสอบส่วนขยาย

ทดสอบส่วนขยาย C ผ่านอินเทอร์เฟซ Python โดยใช้ pytest ไม่จำเป็นต้องใช้เครื่องมือทดสอบ 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)

ตรวจสอบอย่างรวดเร็ว

ฟังก์ชันส่วนขยาย C ต้องส่งคืนค่าใดเพื่อระบุว่ามีการตั้งข้อยกเว้นของ Python แล้ว

สรุป

ส่วนขยาย C ต้องมี: ฟังก์ชันเมธอด (แยกวิเคราะห์อาร์กิวเมนต์และส่งคืน PyObject*) ตารางเมธอด โครงสร้างนิยามโมดูล และ PyInit_name() โยนข้อยกเว้นโดยตั้งค่าด้วย PyErr_SetString แล้วส่งคืน NULL ปล่อย GIL สำหรับงาน C ล้วน

คำถามที่พบบ่อย

บทเรียน “การเขียนโมดูลส่วนขยาย C ของ Python” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การเขียนโมดูลส่วนขยาย C ของ Python” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Python Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Python Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การเขียนโมดูลส่วนขยาย C ของ Python”

สร้างส่วนขยาย .so อย่างง่ายด้วย Python/C API คุณปฏิบัติ Python Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Python Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Python Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “การเขียนโมดูลส่วนขยาย C ของ Python” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Python Academy นี้ได้ไหม

ได้ บทเรียน Python Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เหตุผลที่ต้องใช้ส่วนขยาย C: กรณีใช้งานและข้อแลกเปลี่ยน
  2. ctypes: การเรียกใช้ไลบรารี C จาก Python
  3. cffi: อินเทอร์เฟซฟังก์ชันภายนอกของ C
  4. การเขียนโมดูลส่วนขยาย C ของ Python
← กลับไปที่ Python Academy