PHP
Python

Trang chủ

Tìm hiểu về Extension trong Python và cách sử dụng hiệu quả

Ngôn ngữ Python là một ngôn ngữ lập trình đa mục đích, nổi tiếng với cú pháp dễ đọc, dễ học và tính ứng dụng cao. Trong lĩnh vực phát triển web, Python thường được sử dụng thông qua các framework như Django và Flask để xây dựng các ứng dụng web mạnh mẽ, bảo mật và dễ mở rộng. Trong chuyên mục này, Vietnix không chỉ cung cấp kiến thức nền tảng về ngôn ngữ Python mà còn hướng dẫn chi tiết cách xây dựng các ứng dụng web thực tế, sử dụng các framework phổ biến và áp dụng các kỹ thuật tiên tiến. Vietnix cam kết liên tục cập nhật những bài viết mới nhất về các tính năng mới của Python, các thư viện hỗ trợ hữu ích và những phương pháp tốt nhất, giúp bạn khai thác tối đa sức mạnh của Python và hoàn thiện kỹ năng lập trình web của mình.
html
CSS
javascript
sql
python
php
c
c++
bootstrap
react
mysql
reactjs
vuejs
Javascript Tutorials
25/03/2025
29 phút đọc
Theo dõi Vietnix trên

Tìm hiểu về Extension trong Python và cách sử dụng hiệu quả

Trong Python, Extension cho phép bạn mở rộng khả năng của ngôn ngữ này bằng cách tích hợp mã C/C++ vào trong các dự án Python. Việc này giúp cải thiện hiệu suất và tận dụng được sức mạnh của các thư viện hệ thống đã có. Bài viết này sẽ hướng dẫn bạn về các khái niệm cơ bản, điều kiện cần có, cũng như cách xây dựng và sử dụng Extension trong Python để tối ưu hóa ứng dụng của bạn. Chúng ta sẽ cùng tìm hiểu các bước từ cài đặt, sử dụng cho đến các kỹ thuật xử lý tham số hiệu quả.

Điểm chính cần nắm

  • Further Extension trong Python là gì?: Extension trong Python là cách mở rộng các chức năng của Python bằng C/C++ để tối ưu hiệu suất.
  • Điều kiện tiên quyết để viết extension: Cần cài đặt Python và trình biên dịch C/C++, hiểu Python C API, sử dụng setuptools và có kiến thức về C/C++.
  • Tổng quan về một extension trong Python: Extension trong Python bao gồm các thành phần như file header Python.h, hàm C, phương thức mapping table và hàm khởi tạo module.
  • Xây dựng và cài đặt extension: Hướng dẫn chi tiết về cách xây dựng và cài đặt một extension trong Python.
  • Import và sử dụng extension trong Python: Cách thức import và sử dụng extension sau khi cài đặt.
  • Truyền tham số vào function trong extension: Hướng dẫn cách truyền tham số vào các hàm C trong extension của Python.
  • Trích xuất tham số bằng pyarg_parsetuple: Giới thiệu về việc sử dụng PyArg_ParseTuple để xử lý tham số truyền vào.
  • Hàm Py_BuildValue – Trả về giá trị từ extension: Cách sử dụng hàm Py_BuildValue để trả về giá trị từ extension trong Python.
  • Vietnix – Nhà cung cấp dịch vụ lưu trữ uy tín, tốc độ và bảo mật hàng đầu: Giới thiệu Vietnix như một nhà cung cấp dịch vụ hosting, server và VPS đáng tin cậy tại Việt Nam.
  • FAQ: Phần câu hỏi thường gặp, giải đáp các thắc mắc về việc sử dụng Extension trong Python.

Further Extension trong Python là gì?

Further Extensions trong Python là cách mở rộng khả năng của Python bằng cách tích hợp mã từ các ngôn ngữ lập trình khác như C, C++ hoặc Java. Điều này giúp cải thiện hiệu suất và bổ sung các chức năng mà Python thuần không có. Các phần mở rộng này thường được viết dưới dạng module C và có thể biên dịch thành thư viện chia sẻ. Trên Unix, các thư viện này có đuôi .so (shared object), còn trên Windows, chúng có đuôi .dll (dynamically linked library).

Further Extension trong Python
Further Extension trong Python

1. Cài đặt Python và trình biên dịch C/C++

  • Đảm bảo đã cài đặt Python (Nên dùng phiên bản mới nhất).
  • Cài đặt trình biên dịch:

2. Hiểu về Python C API

Python C API cung cấp các hàm giúp thao tác với đối tượng Python từ C/C++. Việc hiểu rõ API này giúp:

  • Giúp tối ưu hiệu suất khi viết extension.
  • Quản lý bộ nhớ hiệu quả.
  • Xử lý lỗi chính xác.

3. Biết cách sử dụng setuptools

setuptools hỗ trợ biên dịch và đóng gói extension một cách đơn giản. Cài đặt bằng lệnh:

pip install setuptools

Ngoài ra, bạn cần hiểu cách sử dụng các function trong module mở rộng, ví dụ:

static PyObject *MyFunction(PyObject *self, PyObject *args);
static PyObject *MyFunctionWithKeywords(PyObject *self, PyObject *args, PyObject *kw);
static PyObject *MyFunctionWithNoArgs(PyObject *self);

4. Kiến thức về C/C++

Cần có hiểu biết về:

  • Con trỏ và quản lý bộ nhớ.
  • Cách tạo và sử dụng thư viện động (.so trên Linux/macOS, .dll trên Windows).

5. Biết cách biên dịch module mở rộng

  • Viết mã C để định nghĩa các hàm có thể gọi từ Python.
  • Sử dụng setup.py để biên dịch thành thư viện mở rộng có thể nhập vào Python.

Ngoài ra, để bắt đầu viết extension, bạn cần có các tệp tiêu đề của Python.

  • Trên Linux/macOS: Cần cài đặt thêm gói dành cho nhà phát triển (ví dụ: python3-dev trên Debian/Ubuntu).
  • Trên Windows: Các tệp tiêu đề này có sẵn khi cài đặt Python dưới dạng nhị phân.

Cuối cùng, kiến thức vững chắc về C hoặc C++ là điều bắt buộc nếu muốn viết extension cho Python.

Tổng quan về một extension trong Python

Một extension trong Python là một module được viết bằng C hoặc C++, giúp mở rộng chức năng của Python, tối ưu hiệu suất hoặc tích hợp với thư viện bên ngoài. Extension thường được biên dịch thành thư viện động (.so trên Linux/macOS, .dll trên Windows) và có thể được import như một module Python thông thường.

Một module mở rộng trong Python thường bao gồm bốn phần chính:

  • File header Python.h: Cung cấp các định nghĩa cần thiết để làm việc với Python C API.
  • Các hàm C/C++: Chứa các logic xử lý mà Python có thể gọi.
  • Bảng ánh xạ hàm: Liên kết tên hàm trong Python với các hàm C/C++ tương ứng.
  • Hàm khởi tạo module: Được gọi khi module được import vào Python.

File header Python.h trong extension Python

Python.h là tệp tiêu đề quan trọng khi viết extension bằng C/C++ cho Python. Tệp này cung cấp các định nghĩa, kiểu dữ liệu và hàm cần thiết để làm việc với Python C API.

Lý do cần Python.h

  • Cung cấp quyền truy cập vào Python C API để thao tác với đối tượng Python.
  • Hỗ trợ quản lý bộ nhớ của Python, giúp tạo và hủy đối tượng an toàn.
  • Cung cấp macro và hàm để xử lý Exception, gọi Function, và tương tác với Module Python.

Cách sử dụng Python.h

Khi viết extension, tệp này cần được bao gồm ở đầu chương trình:

#include <Python.h>

Tệp Python.h cung cấp quyền truy cập vào API nội bộ của Python, giúp kết nối module C với trình thông dịch. Để tránh lỗi, hãy luôn bao gồm Python.h trước bất kỳ tiêu đề nào khác trong mã nguồn C. Sau đó, bạn có thể định nghĩa các hàm để gọi từ Python.

Hàm C trong extension

Khi viết extension bằng C, bạn cần định nghĩa các hàm có thể được gọi từ Python. Những hàm này có định dạng đặc biệt để tương thích với Python C API.

Định dạng hàm C trong extension Python

Một hàm C trong extension Python thường có dạng sau:

static PyObject* function_name(PyObject *self, PyObject *args) {
    // Xử lý tham số từ Python
    // Thực hiện logic của hàm
    // Trả về kết quả dưới dạng PyObject*
}
  • PyObject* là kiểu trả về chung cho mọi hàm trong module mở rộng.
  • self: Thường được sử dụng trong phương thức của một object, nhưng với hàm thông thường, nó có thể bỏ qua.
  • args: Chứa danh sách đối số được truyền từ Python.

Ví dụ hàm C đơn giản

Hàm sau nhận hai số từ Python và trả về tổng của chúng:

#include <Python.h>

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

Giải thích:

  • PyArg_ParseTuple(args, "ii", &a, &b): Giải mã tham số từ Python thành hai số nguyên.
  • PyLong_FromLong(a + b): Chuyển kết quả về dạng PyObject* để Python có thể hiểu.

Các dạng chữ ký hàm C trong extension

Các chữ ký của việc triển khai C cho các hàm của bạn luôn có một trong ba dạng sau:

static PyObject* MyFunction(PyObject *self, PyObject *args);
static PyObject* MyFunctionWithKeywords(PyObject *self, PyObject *args, PyObject *kw);
static PyObject* MyFunctionWithNoArgs(PyObject *self);
  • Hàm có đối số (MyFunction): Nhận tham số từ Python qua args.
  • Hàm có cả đối số thường và đối số từ khóa (MyFunctionWithKeywords): Nhận tham số positional và keyword arguments từ Python.
  • Hàm không có đối số (MyFunctionWithNoArgs): Không nhận tham số nào từ Python.

Python không có hàm void như trong C. Nếu hàm không cần trả về giá trị, em nên dùng Py_RETURN_NONE; thay vì return Py_None; để tránh rò rỉ bộ nhớ.

Định nghĩa bảng phương thức (Method Table)

Để Python có thể gọi các hàm C này, em cần đăng ký chúng trong một bảng phương thức (PyMethodDef):

static PyMethodDef MyMethods[] = {
    {"add", add, METH_VARARGS, "Cộng hai số nguyên"},
    {NULL, NULL, 0, NULL}
};

Mỗi dòng trong bảng gồm:

  • "add": Tên hàm khi gọi từ Python.
  • add: Con trỏ đến hàm C.
  • METH_VARARGS: Kiểu tham số (ở đây là danh sách đối số).
  • "Cộng hai số nguyên": Chuỗi mô tả hàm (Python dùng help(module.function) để đọc mô tả này).

Hàm khởi tạo module

Cuối cùng, cần một hàm khởi tạo để Python nhận diện module:

static struct PyModuleDef mymodule = {
    PyModuleDef_HEAD_INIT,
    "mymodule",
    "Module mở rộng viết bằng C",
    -1,
    MyMethods
};

PyMODINIT_FUNC PyInit_mymodule(void) {
    return PyModule_Create(&mymodule);
}
  • "mymodule": Tên module khi import vào Python.
  • MyMethods: Danh sách các hàm trong module.

Sau khi biên dịch, module có thể được import vào Python như sau:

import mymodule
print(mymodule.add(3, 5))  # Kết quả: 8

Phương thức mapping table

Trong module mở rộng viết bằng C, mapping table được sử dụng để liên kết tên hàm Python với hàm C tương ứng. Đây là phần quan trọng giúp Python có thể gọi các hàm C trong extension.

Cấu trúc bảng ánh xạ phương thức

Bảng ánh xạ phương thức trong C được định nghĩa bằng struct PyMethodDef, có dạng như sau:

static PyMethodDef ModuleMethods[] = {
    {"function_name", function_pointer, method_flags, "Function description"},
    {NULL, NULL, 0, NULL} // Kết thúc danh sách
};

Trong đó:

  • "function_name": Tên hàm khi gọi từ Python.
  • function_pointer: Con trỏ trỏ đến hàm C thực thi.
  • method_flags: Kiểu tham số hàm nhận.
  • "Function description": Chuỗi mô tả giúp help(module.function_name) hiển thị thông tin hàm.

Các kiểu method_flags trong Python C API

  • METH_VARARGS: Hàm nhận danh sách đối số args.
  • METH_KEYWORDS: Hàm nhận cả argskwargs.
  • METH_NOARGS: Hàm không nhận tham số.
  • METH_O: Hàm nhận một đối số duy nhất.
  • METH_STATICMETH_CLASS: Dùng trong class method.

Ví dụ bảng ánh xạ phương thức

Dưới đây là bảng ánh xạ cho một module có ba hàm:

  • add: Cộng hai số nguyên.
  • greet: Trả về chuỗi chào.
  • info: In ra thông tin module.
#include <Python.h>

// Hàm C đơn giản
static PyObject* add(PyObject *self, PyObject *args) {
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
        return NULL;
    }
    return PyLong_FromLong(a + b);
}

// Hàm không có tham số
static PyObject* greet(PyObject *self) {
    return PyUnicode_FromString("Xin chào từ C extension!");
}

// Hàm nhận một đối số duy nhất
static PyObject* info(PyObject *self, PyObject *arg) {
    if (!PyUnicode_Check(arg)) {
        PyErr_SetString(PyExc_TypeError, "Phải truyền một chuỗi.");
        return NULL;
    }
    return PyUnicode_FromFormat("Thông tin: %S", arg);
}

// Bảng ánh xạ phương thức
static PyMethodDef MyMethods[] = {
    {"add", add, METH_VARARGS, "Cộng hai số nguyên"},
    {"greet", (PyCFunction)greet, METH_NOARGS, "Hàm không có tham số"},
    {"info", info, METH_O, "Nhận một chuỗi và in thông tin"},
    {NULL, NULL, 0, NULL} // Kết thúc danh sách
};

Tích hợp bảng phương thức vào module

Bảng phương thức cần được đăng ký trong struct PyModuleDef:

static struct PyModuleDef mymodule = {
    PyModuleDef_HEAD_INIT,
    "mymodule",
    "Module mở rộng viết bằng C",
    -1,
    MyMethods
};

PyMODINIT_FUNC PyInit_mymodule(void) {
    return PyModule_Create(&mymodule);
}

Sau khi biên dịch, module có thể sử dụng trong Python như sau:

import mymodule

print(mymodule.add(3, 5))  # Output: 8
print(mymodule.greet())    # Output: Xin chào từ C extension!
print(mymodule.info("Extension Python"))  # Output: Thông tin: Extension Python

iconLưu ý

  • Luôn kết thúc bảng bằng {NULL, NULL, 0, NULL} để Python biết điểm dừng.

  • Dùng đúng method_flags để tránh lỗi khi gọi hàm từ Python.

  • Mô tả đầy đủ các phương thức để hỗ trợ help(module.function).

Bảng phương thức là một mảng các cấu trúc PyMethodDef. Mỗi phần tử trong mảng này đại diện cho một phương thức trong module và được định nghĩa theo cấu trúc sau:

struct PyMethodDef {
   char *ml_name;      // Tên của hàm trong Python
   PyCFunction ml_meth; // Con trỏ trỏ đến hàm C thực thi
   int ml_flags;       // Cờ xác định cách hàm nhận tham số
   char *ml_doc;       // Chuỗi mô tả hàm
};

Dưới đây là mô tả các thành viên của cấu trúc này:Dưới đây là mô tả chi tiết về các thành viên của PyMethodDef:

  • ml_name – Tên của hàm khi gọi từ Python.
  • ml_meth – Con trỏ trỏ đến hàm C xử lý logic của phương thức.
  • ml_flags – Xác định cách hàm nhận tham số, có thể là một trong các giá trị:
    • METH_VARARGS: Hàm nhận danh sách đối số.
    • METH_KEYWORDS: Hàm nhận cả args và kwargs.
    • METH_NOARGS: Hàm không có tham số.
    • METH_O: Hàm nhận một đối số duy nhất.
  • ml_doc – Chuỗi mô tả giúp help(module.function_name) hiển thị thông tin chi tiết về hàm. Có thể đặt NULL nếu không cần mô tả.

Ví dụ:

Bảng phương thức luôn cần kết thúc bằng một phần tử có giá trị NULL để Python biết điểm dừng. Phần này sẽ giúp Python ánh xạ chính xác các hàm từ C sang Python, đảm bảo module mở rộng hoạt động đúng như mong đợi.

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, "Mô tả hàm func" },
   { NULL, NULL, 0, NULL } // Kết thúc danh sách
};

Hàm khởi tạo module

Phần cuối cùng trong module mở rộng là hàm khởi tạo. Hàm này sẽ được trình thông dịch Python gọi khi module được tải vào chương trình. Theo quy tắc của Python C API, hàm khởi tạo phải được đặt tên theo mẫu PyInit_<ModuleName>, trong đó <ModuleName> là tên của module.

Hàm khởi tạo cần được xuất từ thư viện được biên dịch. Python định nghĩa PyMODINIT_FUNC để đảm bảo hàm khởi tạo được xử lý đúng trên các môi trường biên dịch khác nhau. Khi viết hàm khởi tạo, chỉ cần sử dụng PyMODINIT_FUNC để khai báo.

Dưới đây là cấu trúc tổng quát của hàm khởi tạo:

PyMODINIT_FUNC PyInit_ModuleName(void) {
    return PyModule_Create(&module_definition);
}

Trong đó:

  • ModuleName là tên module.
  • module_definition là cấu trúc PyModuleDef chứa thông tin về module.

Ví dụ hoàn chỉnh

#include <Python.h>

// Định nghĩa hàm Python
static PyObject* helloworld(PyObject* self) {
    return Py_BuildValue("s", "Hello, Python extensions!!");
}

// Chuỗi mô tả hàm
static char helloworld_docs[] = "helloworld(): Trả về chuỗi chào.\n";

// Bảng ánh xạ phương thức
static PyMethodDef helloworld_funcs[] = {
    {"helloworld", (PyCFunction)helloworld, METH_NOARGS, helloworld_docs},
    {NULL, NULL, 0, NULL}  // Kết thúc danh sách
};

// Định nghĩa module
static struct PyModuleDef helloworld_module = {
    PyModuleDef_HEAD_INIT,
    "helloworld",
    "Mô tả module extension",
    -1,
    helloworld_funcs
};

// Hàm khởi tạo module
PyMODINIT_FUNC PyInit_helloworld(void) {
    return PyModule_Create(&helloworld_module);
}

iconLưu ý

  • Không sử dụng Py_InitModule3, vì API này đã bị loại bỏ trong Python 3.

  • Hàm khởi tạo phải có tiền tố PyInit_.

  • PyModuleDef là bắt buộc để định nghĩa module trong Python 3.

Xây dựng và cài đặt extension

Gói distutils giúp phân phối module Python dễ dàng, bao gồm cả module thuần Python và module mở rộng. Các module này được phân phối dưới dạng mã nguồn, xây dựng và cài đặt thông qua script setup.py.

Tạo file setup.py

Đối với module trên, bạn cần chuẩn bị script setup.py như sau:

from distutils.core import setup, Extension
setup(name='helloworld', version='1.0', \
   ext_modules=[Extension('helloworld', ['hello.c'])])

Cài đặt module

Bây giờ, sử dụng lệnh sau, lệnh này sẽ thực hiện tất cả các bước biên dịch và liên kết cần thiết, với các lệnh và cờ trình biên dịch và trình liên kết phù hợp, và sao chép thư viện động kết quả vào một thư mục thích hợp:

$ python setup.py install

Trên Unix, có thể cần quyền root:

sudo python setup.py install

Trên các hệ thống dựa trên Unix, bạn rất có thể cần phải chạy lệnh này với quyền root để có quyền ghi vào thư mục site-packages. Điều này thường không phải là vấn đề trên Windows.

Import và sử dụng extension trong Python

Sau khi đã xây dựng và cài đặt extension thành công, bạn có thể import module đó vào script Python như một module thông thường. Ví dụ, với module helloworld, bạn có thể sử dụng như sau:

import helloworld

# Gọi hàm đã định nghĩa trong extension
print(helloworld.helloworld())

Khi chạy đoạn mã trên, bạn sẽ thấy kết quả được hiển thị như bên dưới.

Hello, Python extensions!!

Điều này chứng tỏ extension đã được cài đặt đúng cách và có thể hoạt động như một module Python thông thường. Nếu gặp lỗi ModuleNotFoundError, hãy kiểm tra lại quá trình cài đặt hoặc đảm bảo rằng module nằm trong thư mục có thể import được.

Truyền tham số vào function trong extension

Khi xây dựng extension trong Python, em có thể muốn định nghĩa các function chấp nhận tham số. Để làm được điều này, cần sử dụng một trong các chữ ký (signature) khác nhau cho function trong C. Ví dụ, một function nhận tham số sẽ được định nghĩa như sau:

static PyObject *module_func(PyObject *self, PyObject *args) {
    /* Parse args and xử lý dữ liệu tại đây */
    Py_RETURN_NONE;
}

Định nghĩa function với tham số

Để Python có thể gọi function này, cần khai báo nó trong bảng phương thức:

static PyMethodDef module_methods[] = {
    { "func", (PyCFunction)module_func, METH_VARARGS, NULL },
    { NULL, NULL, 0, NULL } // Kết thúc danh sách
};

Trong đó:

  • METH_VARARGS cho phép function nhận nhiều tham số.
  • NULL ở cuối danh sách để đánh dấu kết thúc.

Trích xuất tham số bằng pyarg_parsetuple

Bạn có thể sử dụng hàm API PyArg_ParseTuple để trích xuất các đối số từ con trỏ PyObject duy nhất được truyền vào hàm C. Tham số đầu tiên của PyArg_ParseTupleargs, đối tượng chứa danh sách đối số cần phân tích. Tham số thứ hai là một chuỗi định dạng, dùng để mô tả cách các đối số được mong đợi. Mỗi đối số sẽ được biểu diễn bằng một hoặc nhiều ký tự trong chuỗi định dạng, cụ thể như sau:

static PyObject *module_func(PyObject *self, PyObject *args) {
    int i;
    double d;
    char *s;
    
    if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) {
        return NULL;
    }

    /* Xử lý dữ liệu nhận được */
    Py_RETURN_NONE;
}
  • "ids" là chuỗi định dạng, trong đó:
    • iint (số nguyên)
    • ddouble (số thực)
    • schar* (chuỗi ký tự)
  • Các tham số sau "ids" là địa chỉ của biến sẽ chứa dữ liệu sau khi phân tích.

Khi biên dịch phiên bản mới của module và nhập lại, bạn có thể gọi hàm mới với số lượng đối số tùy ý, thuộc bất kỳ kiểu dữ liệu nào.

module.func(1, s="three", d=2.0)
module.func(i=1, d=2.0, s="three")
module.func(s="three", d=2.0, i=1)

Danh sách định dạng PyArg_ParseTuple

Hàm PyArg_ParseTuple được sử dụng để trích xuất tham số từ một tuple Python được truyền vào function C. Chữ ký của hàm như sau:

int PyArg_ParseTuple(PyObject *args, const char *format, ...);
  • Trả về 0 nếu có lỗi, trả về giá trị khác 0 nếu thành công.
  • tuple là tham số thứ hai của function C, chứa các đối số được truyền từ Python.
  • format là chuỗi mô tả kiểu dữ liệu của các tham số mong đợi.

Dưới đây là danh sách các mã định dạng cho hàm PyArg_ParseTuple:

Kiểu dữ liệu CÝ nghĩa
ccharChuỗi Python độ dài 1 → ký tự C.
ddoubleFloat Python → double C.
ffloatFloat Python → float C.
iintInteger Python → int C.
llongInteger Python → long C.
Llong longInteger Python → long long C.
OPyObject*Nhận một tham chiếu đến đối tượng Python.
Schar*Chuỗi Python không có null embedded → C char*.
s#char* + intChuỗi Python bất kỳ → địa chỉ + độ dài.
t#char* + intBộ đệm đơn phân đoạn (read-only).
uPy_UNICODE*Chuỗi Unicode Python không nhúng null → C Py_UNICODE*.
u#Py_UNICODE* + intUnicode Python → địa chỉ + độ dài.
w#char* + intBộ đệm đơn phân đoạn (read/write).
zchar*Giống s, nhưng chấp nhận None (trả về NULL).
z#char* + intGiống s#, nhưng chấp nhận None.
(...)Phụ thuộc nội dungTuple Python được xem như các đối số riêng lẻ.
``
:Kết thúc định dạng, tiếp theo là tên function (cho thông báo lỗi).
;Kết thúc định dạng, tiếp theo là toàn bộ thông báo lỗi.
Danh sách định dạng PyArg_ParseTuple

Sử dụng PyArg_ParseTuple để xử lý tham số

Hàm Py_BuildValue nhận một chuỗi định dạng tương tự như PyArg_ParseTuple. Tuy nhiên, thay vì truyền địa chỉ của các biến để lưu giá trị, bạn sẽ truyền trực tiếp các giá trị thực tế cần tạo. Dưới đây là ví dụ triển khai một hàm add trong C, tương đương với một function trong Python:

Triển khai trong C

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

Tương đương trong Python

def add(a, b):
   return a + b

Bạn cũng có thể trả về nhiều giá trị từ hàm C bằng cách sử dụng Py_BuildValue. Ví dụ, hàm dưới đây trả về cả tổng và hiệu của hai số:

Triển khai trong C

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

Tương đương trong Python

def add_subtract(a, b):
   return a + b, a - b

Bằng cách sử dụng PyArg_ParseTuplePy_BuildValue, bạn có thể dễ dàng giao tiếp giữa Python và C, giúp module mở rộng hoạt động mượt mà hơn.

Hàm Py_BuildValue – Trả về giá trị từ extension

Hàm Py_BuildValue giúp tạo các đối tượng Python từ các giá trị trong C và trả về dưới dạng PyObject*. Chữ ký của hàm như sau:

PyObject* Py_BuildValue(char* format,...)
  • format là một chuỗi mô tả kiểu dữ liệu của đối tượng Python cần xây dựng.
  • Các tham số tiếp theo là giá trị C tương ứng.
  • Giá trị trả về là một tham chiếu mới đến đối tượng Python được tạo.

Bảng mã định dạng thường dùng

CodeC typeÝ nghĩa
ccharChuyển một ký tự C thành chuỗi Python có độ dài 1.
ddoubleChuyển double trong C thành số float trong Python.
ffloatChuyển float trong C thành số float trong Python.
iintChuyển int trong C thành số int trong Python.
llongChuyển long trong C thành số int trong Python.
NPyObject*Trả về một đối tượng Python mà không tăng reference count.
OPyObject*Trả về một đối tượng Python và tăng reference count (INCREF).
O&convert+void*Chuyển đổi tùy chỉnh.
schar*Chuyển chuỗi C (char* kết thúc bằng \0) thành chuỗi Python. Nếu NULL, trả về None.
s#char*+intChuyển chuỗi C có độ dài cụ thể thành chuỗi Python. Nếu NULL, trả về None.
uPy_UNICODE*Chuyển wchar_t* kết thúc bằng \0 trong C thành chuỗi Unicode Python. Nếu NULL, trả về None.
u#Py_UNICODE*+intChuyển wchar_t* với độ dài cụ thể thành chuỗi Unicode Python. Nếu NULL, trả về None.
w#char*+intChuyển bộ đệm có thể đọc/ghi thành tuple chứa địa chỉ và độ dài.
zchar*Giống s, nhưng chấp nhận None (char* trong C có thể là NULL).
z#char*+intGiống s#, nhưng chấp nhận None.
(…)as per …Tạo tuple Python từ các giá trị C.
[…]as per …Tạo danh sách Python từ các giá trị C.
{…}as per …Tạo từ điển Python từ các cặp khóa-giá trị trong C.
Hàm Py_BuildValue – Trả về giá trị từ extension

Ví dụ:

PyObject *dict = Py_BuildValue("{issi}", 23, "zig", "zag", 42);
// Tương đương với Python: {23: "zig", "zag": 42}

Hàm Py_BuildValue giúp dễ dàng tạo các kiểu dữ liệu Python từ C, hỗ trợ tốt cho việc viết extension module.

Vietnix – Nhà cung cấp dịch vụ lưu trữ uy tín, tốc độ và bảo mật hàng đầu

Vietnix là một trong những đơn vị cung cấp dịch vụ cho thuê máy chủ (server), hosting, VPS và tên miền (domain) hàng đầu tại Việt Nam. Với mục tiêu mang đến giải pháp lưu trữ hiệu quả và bảo mật tối ưu, Vietnix cam kết cung cấp dịch vụ chất lượng vượt trội cùng đội ngũ hỗ trợ kỹ thuật chuyên nghiệp, sẵn sàng hỗ trợ 24/7. Hơn 80.000 khách hàng đã lựa chọn Vietnix nhờ vào dịch vụ cho thuê máy chủ đáng tin cậy, giúp tối ưu hóa hiệu suất và bảo mật dữ liệu cho doanh nghiệp.

Thông tin liên hệ:

  • Hotline: 18001093
  • Email: sales@vietnix.com.vn
  • Địa chỉ: 265 Hồng Lạc, Phường 10, Quận Tân Bình, Thành Phố Hồ Chí Minh.
  • Website: https://vietnix.vn/

Câu hỏi thường gặp

Làm sao để sử dụng Python C API để tạo một mô-đun với nhiều hàm, và làm thế nào để kết hợp chúng trong một extension duy nhất?

Việc tạo một mô-đun với nhiều hàm trong extension Python bao gồm việc định nghĩa từng hàm riêng biệt và khai báo một danh sách các hàm trong cấu trúc PyMethodDef để nhóm chúng lại với nhau.

Tại sao cần phải biên dịch lại mỗi lần chỉnh sửa extension trong Python và có cách nào để giảm bớt việc này không?

Việc biên dịch lại mỗi lần chỉnh sửa là cần thiết vì Python không tự động nhận diện thay đổi trong mã C. Tuy nhiên, có thể sử dụng công cụ như Cython hoặc các kỹ thuật tối ưu hóa biên dịch để giảm thời gian xây dựng lại mô-đun.

Có phải tất cả extension Python đều cần phải sử dụng Python C API, hay có thể dùng cách khác?

Không, ngoài Python C API, bạn còn có thể sử dụng các công cụ như Cython hoặc PyPy để tạo extension mà không phải làm việc trực tiếp với mã C.

Python C API và Cython có điểm gì khác biệt khi tạo extension?

Python C API yêu cầu lập trình viên làm việc trực tiếp với mã C, trong khi Cython là một công cụ giúp viết extension Python mà không cần phải hiểu sâu về C.

Có thể sử dụng extension Python cho các tác vụ xử lý dữ liệu lớn như học máy hoặc phân tích dữ liệu không?

Có thể, extension Python có thể được sử dụng để tối ưu hóa các tác vụ tính toán nặng, chẳng hạn như học máy hoặc phân tích dữ liệu, giúp cải thiện hiệu suất khi làm việc với dữ liệu lớn.

Lời kết

Hy vọng qua bài viết này, bạn đã hiểu rõ hơn về cách làm việc với Extension trong Python và những lợi ích mà chúng mang lại. Việc tích hợp mã C/C++ giúp mở rộng hiệu suất cho các ứng dụng Python của bạn một cách đáng kể. Nếu bạn có bất cứ thắc mắc hay cần hỗ trợ gì, hãy để lại bình luận bên dưới, mình sẽ hỗ trợ nhanh nhất có thể. Cảm ơn bạn đã đọc!

Mọi người cũng xem:

Cao Lê Viết Tiến

PHP Leader
tại
Vietnix

Kết nối với mình qua

Icon Quote
Icon Quote

Học lập trình online cùng vietnix

Học lập trình online cùng Vietnix

PHPXem thêmThu gọn