Hướng dẫn are python modules written in c? - các mô-đun python được viết bằng c?

5

Mới! Lưu câu hỏi hoặc câu trả lời và sắp xếp nội dung yêu thích của bạn. Tìm hiểu thêm.
Learn more.

Tôi dự định viết một trình soạn thảo văn bản nhỏ trong Python. Do đó, tôi cần một thư viện xử lý văn bản/thao tác nhanh, tốt nhất là được viết bằng C vì lý do hiệu suất.

Làm thế nào để tôi biết những mô -đun Python nào được viết bằng C dưới mui xe?

Đã hỏi ngày 1 tháng 7 năm 2013 lúc 13:32Jul 1, 2013 at 13:32

4

Giống như Gene được đề xuất, cách tốt nhất là xem xét các nguồn và tìm ra mô -đun nào được thực hiện trong C.

Bạn có thể kiểm tra thuộc tính của mô -đun

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
2:

>>> import math
>>> print[math.__file__]
/usr/lib/python2.7/lib-dynload/math.so

Trong ví dụ này, đó là một tệp

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
3 đã được tải, do đó mô -đun này được triển khai bằng ngôn ngữ được biên dịch [rất có thể là trong C].

Đã trả lời ngày 1 tháng 7 năm 2013 lúc 13:39Jul 1, 2013 at 13:39

Lastmikoilastmikoilastmikoi

3341 Huy hiệu bạc9 Huy hiệu đồng1 silver badge9 bronze badges

1

Thật dễ dàng để thêm các mô-đun tích hợp mới vào Python, nếu bạn biết cách lập trình trong C. Các mô-đun mở rộng như vậy có thể làm hai việc không thể thực hiện trực tiếp trong Python: chúng có thể triển khai các loại đối tượng tích hợp mới, Và họ có thể gọi các chức năng thư viện C và các cuộc gọi hệ thống.

Để hỗ trợ các tiện ích mở rộng, API Python [giao diện lập trình viên ứng dụng] xác định một tập hợp các hàm, macro và biến cung cấp quyền truy cập vào hầu hết các khía cạnh của hệ thống thời gian chạy Python. API Python được tích hợp trong tệp nguồn C bằng cách bao gồm tiêu đề

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
4.

Việc tổng hợp một mô -đun mở rộng phụ thuộc vào mục đích sử dụng cũng như trên thiết lập hệ thống của bạn; Chi tiết được đưa ra trong các chương sau.

Ghi chú

Giao diện mở rộng C dành riêng cho CPython và các mô -đun mở rộng không hoạt động trên các triển khai Python khác. Trong nhiều trường hợp, có thể tránh viết các phần mở rộng C và bảo tồn tính di động cho các triển khai khác. Ví dụ: nếu trường hợp sử dụng của bạn đang gọi các chức năng thư viện C hoặc cuộc gọi hệ thống, bạn nên xem xét sử dụng mô -đun

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
5 hoặc thư viện CFFI thay vì viết mã C tùy chỉnh. Các mô -đun này cho phép bạn viết mã Python vào giao diện với mã C và có khả năng di động hơn giữa các triển khai Python hơn là viết và biên dịch mô -đun mở rộng C.

1.1. Một ví dụ đơn giảnA Simple Example¶

Hãy tạo một mô-đun mở rộng có tên

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
6 [món ăn yêu thích của người hâm mộ Monty Python] và giả sử chúng tôi muốn tạo giao diện Python cho chức năng thư viện C . Chúng tôi muốn chức năng này được gọi từ Python như sau:

>>> import spam
>>> status = spam.system["ls -l"]

Bắt đầu bằng cách tạo một tệp

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
8. .

Hai dòng đầu tiên của tệp của chúng tôi có thể là:

#define PY_SSIZE_T_CLEAN
#include 

trong đó kéo trong API Python [bạn có thể thêm một nhận xét mô tả mục đích của mô -đun và thông báo bản quyền nếu bạn muốn].

Ghi chú

Vì Python có thể xác định một số định nghĩa tiền xử lý ảnh hưởng đến các tiêu đề tiêu chuẩn trên một số hệ thống, bạn phải bao gồm

static PyObject *SpamError;
3 trước khi có bất kỳ tiêu đề tiêu chuẩn nào được bao gồm.

Bạn nên luôn định nghĩa

static PyObject *SpamError;
4 trước khi bao gồm
static PyObject *SpamError;
3. Xem trích xuất các tham số trong các chức năng mở rộng để biết mô tả về macro này.Extracting Parameters in Extension Functions for a description of this macro.

Tất cả các ký hiệu có thể nhìn thấy người dùng được xác định bởi

static PyObject *SpamError;
3 đều có tiền tố là
static PyObject *SpamError;
7 hoặc
static PyObject *SpamError;
8, ngoại trừ các ký hiệu được xác định trong các tệp tiêu đề tiêu chuẩn. Để thuận tiện và vì chúng được sử dụng rộng rãi bởi trình thông dịch Python,
static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
4 bao gồm một vài tệp tiêu đề tiêu chuẩn:
PyMODINIT_FUNC
PyInit_spam[void]
{
    PyObject *m;

    m = PyModule_Create[&spammodule];
    if [m == NULL]
        return NULL;

    SpamError = PyErr_NewException["spam.error", NULL, NULL];
    Py_XINCREF[SpamError];
    if [PyModule_AddObject[m, "error", SpamError] >> import spam
>>> status = spam.system["ls -l"]
10 nên được khai báo đúng là
>>> import spam
>>> status = spam.system["ls -l"]
12].

Câu lệnh tiếp theo là một cuộc gọi đến hàm UNIX

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
7, chuyển nó vào chuỗi chúng tôi vừa nhận được từ
PyMODINIT_FUNC
PyInit_spam[void]
{
    PyObject *m;

    m = PyModule_Create[&spammodule];
    if [m == NULL]
        return NULL;

    SpamError = PyErr_NewException["spam.error", NULL, NULL];
    Py_XINCREF[SpamError];
    if [PyModule_AddObject[m, "error", SpamError] >> import spam
>>> status = spam.system["ls -l"]
15 của chúng tôi phải trả về giá trị của
>>> import spam
>>> status = spam.system["ls -l"]
16 dưới dạng đối tượng Python. Điều này được thực hiện bằng cách sử dụng hàm
return PyLong_FromLong[sts];
6.

return PyLong_FromLong[sts];

Trong trường hợp này, nó sẽ trả về một đối tượng số nguyên. [Vâng, ngay cả các số nguyên là đối tượng trên đống trong Python!]

Nếu bạn có hàm C trả về không có đối số hữu ích [hàm trả về

>>> import spam
>>> status = spam.system["ls -l"]
18], hàm python tương ứng phải trả về
>>> import spam
>>> status = spam.system["ls -l"]
19. Bạn cần thành ngữ này để làm như vậy [được thực hiện bởi macro
>>> import spam
>>> status = spam.system["ls -l"]
20]:

Py_INCREF[Py_None];
return Py_None;

>>> import spam
>>> status = spam.system["ls -l"]
21 là tên C cho đối tượng Python đặc biệt
>>> import spam
>>> status = spam.system["ls -l"]
19. Nó là một đối tượng Python chính hãng chứ không phải là một con trỏ
static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    if [sts >> import spam
>>> status = spam.system["ls -l"]
24 được gọi từ các chương trình Python. Đầu tiên, chúng ta cần liệt kê tên và địa chỉ của nó trong bảng phương thức của người dùng:

>>> import spam
>>> status = spam.system["ls -l"]
0

Lưu ý mục thứ ba [

>>> import spam
>>> status = spam.system["ls -l"]
25]. Đây là một lá cờ nói với trình thông dịch quy ước gọi được sử dụng cho hàm C. Thông thường nó luôn luôn là
>>> import spam
>>> status = spam.system["ls -l"]
25 hoặc
>>> import spam
>>> status = spam.system["ls -l"]
27; Giá trị của
>>> import spam
>>> status = spam.system["ls -l"]
28 có nghĩa là một biến thể lỗi thời của
PyMODINIT_FUNC
PyInit_spam[void]
{
    PyObject *m;

    m = PyModule_Create[&spammodule];
    if [m == NULL]
        return NULL;

    SpamError = PyErr_NewException["spam.error", NULL, NULL];
    Py_XINCREF[SpamError];
    if [PyModule_AddObject[m, "error", SpamError] >> import spam
>>> status = spam.system["ls -l"]
25, hàm sẽ mong đợi các tham số cấp độ python được truyền vào dưới dạng một bộ dữ liệu được chấp nhận để phân tích thông qua
PyMODINIT_FUNC
PyInit_spam[void]
{
    PyObject *m;

    m = PyModule_Create[&spammodule];
    if [m == NULL]
        return NULL;

    SpamError = PyErr_NewException["spam.error", NULL, NULL];
    Py_XINCREF[SpamError];
    if [PyModule_AddObject[m, "error", SpamError] >> import spam
>>> status = spam.system["ls -l"]
32 có thể được đặt trong trường thứ ba nếu các đối số từ khóa sẽ được truyền đến hàm. Trong trường hợp này, hàm C sẽ chấp nhận tham số
>>> import spam
>>> status = spam.system["ls -l"]
33 thứ ba sẽ là từ điển của các từ khóa. Sử dụng
>>> import spam
>>> status = spam.system["ls -l"]
34 để phân tích các đối số cho một hàm như vậy.

Bảng phương thức phải được tham chiếu trong cấu trúc định nghĩa mô -đun:

>>> import spam
>>> status = spam.system["ls -l"]
1

Cấu trúc này, đến lượt nó, phải được chuyển cho trình thông dịch trong chức năng khởi tạo mô -đun. Hàm khởi tạo phải được đặt tên

>>> import spam
>>> status = spam.system["ls -l"]
35, trong đó tên là tên của mô-đun và phải là mục duy nhất không phải

>>> import spam
>>> status = spam.system["ls -l"]
2

Lưu ý rằng pymodinit_func tuyên bố hàm là loại trả về

>>> import spam
>>> status = spam.system["ls -l"]
33, khai báo bất kỳ khai báo liên kết đặc biệt nào theo yêu cầu của nền tảng và đối với C ++ khai báo chức năng là
>>> import spam
>>> status = spam.system["ls -l"]
38.

Khi chương trình Python nhập mô -đun

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
6 lần đầu tiên,
Py_INCREF[Py_None];
return Py_None;
8 được gọi. .
>>> import spam
>>> status = spam.system["ls -l"]
41 trả về một con trỏ cho đối tượng mô -đun mà nó tạo ra. Nó có thể hủy bỏ với một lỗi nghiêm trọng đối với một số lỗi nhất định hoặc trả về
static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    if [sts >> import spam
>>> status = spam.system["ls -l"]
45.

Khi nhúng Python, hàm

Py_INCREF[Py_None];
return Py_None;
8 không được gọi tự động trừ khi có một mục nhập trong bảng
>>> import spam
>>> status = spam.system["ls -l"]
47. Để thêm mô -đun vào bảng khởi tạo, hãy sử dụng
>>> import spam
>>> status = spam.system["ls -l"]
48, tùy chọn theo sau là nhập mô -đun:

>>> import spam
>>> status = spam.system["ls -l"]
3

Ghi chú

Loại bỏ các mục từ

>>> import spam
>>> status = spam.system["ls -l"]
45 hoặc nhập các mô -đun biên dịch vào nhiều phiên dịch viên trong một quy trình [hoặc theo
>>> import spam
>>> status = spam.system["ls -l"]
50 mà không cần can thiệp
>>> import spam
>>> status = spam.system["ls -l"]
51] có thể tạo ra sự cố cho một số mô -đun mở rộng. Các tác giả mô -đun mở rộng nên thận trọng khi khởi tạo các cấu trúc dữ liệu nội bộ.

Một mô -đun ví dụ đáng kể hơn được bao gồm trong phân phối nguồn Python là

>>> import spam
>>> status = spam.system["ls -l"]
52. Tệp này có thể được sử dụng làm mẫu hoặc đơn giản là đọc làm ví dụ.

Ghi chú

Loại bỏ các mục từ

>>> import spam
>>> status = spam.system["ls -l"]
45 hoặc nhập các mô -đun biên dịch vào nhiều phiên dịch viên trong một quy trình [hoặc theo
>>> import spam
>>> status = spam.system["ls -l"]
50 mà không cần can thiệp
>>> import spam
>>> status = spam.system["ls -l"]
51] có thể tạo ra sự cố cho một số mô -đun mở rộng. Các tác giả mô -đun mở rộng nên thận trọng khi khởi tạo các cấu trúc dữ liệu nội bộ.PEP 489.

Một mô -đun ví dụ đáng kể hơn được bao gồm trong phân phối nguồn Python là
>>> import spam
>>> status = spam.system["ls -l"]
52. Tệp này có thể được sử dụng làm mẫu hoặc đơn giản là đọc làm ví dụ.
Compilation and Linkage¶

Không giống như ví dụ

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
6 của chúng tôi,
>>> import spam
>>> status = spam.system["ls -l"]
54 sử dụng khởi tạo nhiều pha [mới trong Python 3.5], trong đó cấu trúc pymoduledef được trả về từ
>>> import spam
>>> status = spam.system["ls -l"]
55 và tạo mô-đun được để lại cho máy móc nhập khẩu. Để biết chi tiết về khởi tạo đa pha, xem PEP 489.Building C and C++ Extensions] and additional information that pertains only to building on Windows [chapter Building C and C++ Extensions on Windows] for more information about this.

Nếu bạn có thể sử dụng tải động, hoặc nếu bạn muốn biến mô -đun của mình thành một phần vĩnh viễn của trình thông dịch Python, bạn sẽ phải thay đổi thiết lập cấu hình và xây dựng lại trình thông dịch. May mắn thay, điều này rất đơn giản trên UNIX: Chỉ cần đặt tệp của bạn [ví dụ

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
8] trong thư mục
>>> import spam
>>> status = spam.system["ls -l"]
57 của phân phối nguồn chưa đóng gói, thêm một dòng vào tệp
>>> import spam
>>> status = spam.system["ls -l"]
58 Mô tả tệp của bạn:

và xây dựng lại thông dịch viên bằng cách chạy thực hiện trong thư mục Toplevel. Bạn cũng có thể chạy Make trong thư mục con

>>> import spam
>>> status = spam.system["ls -l"]
57, nhưng trước tiên bạn phải xây dựng lại
>>> import spam
>>> status = spam.system["ls -l"]
60 ở đó bằng cách chạy ‘Makefile. [Điều này là cần thiết mỗi khi bạn thay đổi tệp
>>> import spam
>>> status = spam.system["ls -l"]
61.]make in the toplevel directory. You can also run make in the
>>> import spam
>>> status = spam.system["ls -l"]
57 subdirectory, but then you must first rebuild
>>> import spam
>>> status = spam.system["ls -l"]
60 there by running ‘make Makefile’. [This is necessary each time you change the
>>> import spam
>>> status = spam.system["ls -l"]
61 file.]

Nếu mô -đun của bạn yêu cầu các thư viện bổ sung để liên kết, chúng cũng có thể được liệt kê trên dòng trong tệp cấu hình, ví dụ::

1.6. Gọi các chức năng Python từ C¶Calling Python Functions from C¶

Cho đến nay, chúng tôi đã tập trung vào việc làm cho các chức năng C có thể gọi từ Python. Điều ngược lại cũng hữu ích: gọi các hàm Python từ C. Điều này đặc biệt là trường hợp của các thư viện hỗ trợ các chức năng gọi lại của các cuộc gọi lại. Nếu giao diện C sử dụng các cuộc gọi lại, Python tương đương thường cần cung cấp cơ chế gọi lại cho lập trình viên Python; Việc triển khai sẽ yêu cầu gọi các chức năng gọi lại Python từ gọi lại C. Công dụng khác cũng có thể tưởng tượng được.

May mắn thay, trình thông dịch python dễ dàng được gọi là đệ quy và có một giao diện tiêu chuẩn để gọi hàm python. .

Gọi một chức năng Python là dễ dàng. Đầu tiên, chương trình Python bằng cách nào đó phải chuyển cho bạn đối tượng chức năng Python. Bạn nên cung cấp một chức năng [hoặc một số giao diện khác] để làm điều này. Khi hàm này được gọi, hãy lưu một con trỏ vào đối tượng chức năng Python [hãy cẩn thận với

if [!PyArg_ParseTuple[args, "s", &command]]
    return NULL;
1 nó!] Trong một biến toàn cầu - hoặc bất cứ nơi nào bạn thấy phù hợp. Ví dụ: hàm sau có thể là một phần của định nghĩa mô -đun:

>>> import spam
>>> status = spam.system["ls -l"]
4

Hàm này phải được đăng ký với trình thông dịch bằng cờ

>>> import spam
>>> status = spam.system["ls -l"]
25; Điều này được mô tả trong phần Bảng phương thức mô -đun và chức năng khởi tạo. Hàm
PyMODINIT_FUNC
PyInit_spam[void]
{
    PyObject *m;

    m = PyModule_Create[&spammodule];
    if [m == NULL]
        return NULL;

    SpamError = PyErr_NewException["spam.error", NULL, NULL];
    Py_XINCREF[SpamError];
    if [PyModule_AddObject[m, "error", SpamError] > import spam
>>> status = spam.system["ls -l"]
67 và
Py_INCREF[Py_None];
return Py_None;
0 tăng/giảm số lượng tham chiếu của một đối tượng và an toàn khi có các con trỏ
static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    if [sts > import spam
>>> status = spam.system["ls -l"]
71. Hàm này có hai đối số, cả hai gợi ý đến các đối tượng Python tùy ý: hàm Python và danh sách đối số. Danh sách đối số phải luôn là một đối tượng tuple, có độ dài là số lượng đối số. Để gọi hàm Python không có đối số, hãy vượt qua trong
static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    if [sts >> import spam
>>> status = spam.system["ls -l"]
73 Trả về một tuple khi chuỗi định dạng của nó bao gồm các mã định dạng bằng 0 hoặc nhiều hơn giữa các dấu ngoặc đơn. Ví dụ:

>>> import spam
>>> status = spam.system["ls -l"]
5

>>> import spam
>>> status = spam.system["ls -l"]
71 Trả về một con trỏ đối tượng Python: Đây là giá trị trả về của hàm Python.
>>> import spam
>>> status = spam.system["ls -l"]
71 là trung lập tham chiếu của người Hồi giáo đối với các lập luận của nó. Trong ví dụ, một tuple mới đã được tạo để đóng vai trò là danh sách đối số, được ____ 91-ed ngay sau cuộc gọi
>>> import spam
>>> status = spam.system["ls -l"]
71.

Giá trị trả lại của

>>> import spam
>>> status = spam.system["ls -l"]
71 là mới mới: đó là một đối tượng hoàn toàn mới hoặc đó là một đối tượng hiện có có số lượng tham chiếu đã được tăng lên. Vì vậy, trừ khi bạn muốn lưu nó trong một biến toàn cầu, bạn nên bằng cách nào đó
Py_INCREF[Py_None];
return Py_None;
1 kết quả, thậm chí [đặc biệt!] Nếu bạn không quan tâm đến giá trị của nó.

Tuy nhiên, trước khi bạn làm điều này, điều quan trọng là phải kiểm tra xem giá trị trả về không phải là

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    if [sts >> import spam
>>> status = spam.system["ls -l"]
71 được gọi từ Python, thì giờ đây nó sẽ trả lại chỉ báo lỗi cho trình gọi Python của nó, do đó trình thông dịch có thể in dấu vết ngăn xếp hoặc mã Python gọi có thể xử lý ngoại lệ. Nếu điều này là không thể hoặc mong muốn, ngoại lệ nên được xóa bằng cách gọi
return PyLong_FromLong[sts];
0. Ví dụ:

>>> import spam
>>> status = spam.system["ls -l"]
6

Tùy thuộc vào giao diện mong muốn vào chức năng gọi lại Python, bạn cũng có thể phải cung cấp một danh sách đối số cho

>>> import spam
>>> status = spam.system["ls -l"]
71. Trong một số trường hợp, danh sách đối số cũng được cung cấp bởi chương trình Python, thông qua cùng một giao diện đã chỉ định chức năng gọi lại. Sau đó, nó có thể được lưu và sử dụng theo cách tương tự như đối tượng hàm. Trong các trường hợp khác, bạn có thể phải xây dựng một tuple mới để vượt qua như danh sách đối số. Cách đơn giản nhất để làm điều này là gọi
>>> import spam
>>> status = spam.system["ls -l"]
73. Ví dụ: nếu bạn muốn truyền mã sự kiện tích hợp, bạn có thể sử dụng mã sau:

>>> import spam
>>> status = spam.system["ls -l"]
7

Lưu ý vị trí của

>>> import spam
>>> status = spam.system["ls -l"]
85 ngay sau cuộc gọi, trước khi kiểm tra lỗi! Cũng lưu ý rằng nói đúng mã này không hoàn thành:
>>> import spam
>>> status = spam.system["ls -l"]
73 có thể hết bộ nhớ và điều này cần được kiểm tra.

Bạn cũng có thể gọi một hàm với các đối số từ khóa bằng cách sử dụng

>>> import spam
>>> status = spam.system["ls -l"]
87, hỗ trợ các đối số và đối số từ khóa. Như trong ví dụ trên, chúng tôi sử dụng
>>> import spam
>>> status = spam.system["ls -l"]
73 để xây dựng từ điển.

>>> import spam
>>> status = spam.system["ls -l"]
8

1.8. Thông số từ khóa cho các chức năng mở rộngKeyword Parameters for Extension Functions¶

Hàm

>>> import spam
>>> status = spam.system["ls -l"]
34 được khai báo như sau:

>>> import spam
>>> status = spam.system["ls -l"]
9

Các tham số ARG và định dạng giống hệt với hàm của hàm

PyMODINIT_FUNC
PyInit_spam[void]
{
    PyObject *m;

    m = PyModule_Create[&spammodule];
    if [m == NULL]
        return NULL;

    SpamError = PyErr_NewException["spam.error", NULL, NULL];
    Py_XINCREF[SpamError];
    if [PyModule_AddObject[m, "error", SpamError] >> import spam
>>> status = spam.system["ls -l"]
34 trả về đúng, nếu không nó sẽ trả về sai và làm tăng một ngoại lệ thích hợp.

Ghi chú

Các bộ dữ liệu lồng nhau không thể được phân tích cú pháp khi sử dụng các đối số từ khóa! Các tham số từ khóa được truyền trong đó không có trong danh sách kwlist sẽ khiến

>>> import spam
>>> status = spam.system["ls -l"]
93 được nâng lên.

Dưới đây là một mô -đun ví dụ sử dụng các từ khóa, dựa trên một ví dụ của Geoff Philbrick [Philbrick@hks.com]:@hks.com]:

#define PY_SSIZE_T_CLEAN
#include 
0

1.9. Xây dựng các giá trị tùy ýBuilding Arbitrary Values¶

Hàm này là đối tác của

PyMODINIT_FUNC
PyInit_spam[void]
{
    PyObject *m;

    m = PyModule_Create[&spammodule];
    if [m == NULL]
        return NULL;

    SpamError = PyErr_NewException["spam.error", NULL, NULL];
    Py_XINCREF[SpamError];
    if [PyModule_AddObject[m, "error", SpamError]  import spam
>>> status = spam.system["ls -l"]
73 không phải lúc nào cũng xây dựng một tuple. Nó chỉ xây dựng một bộ tuple nếu chuỗi định dạng của nó chứa hai hoặc nhiều đơn vị định dạng. Nếu chuỗi định dạng trống, nó sẽ trả về
>>> import spam
>>> status = spam.system["ls -l"]
19; Nếu nó chứa chính xác một đơn vị định dạng, nó sẽ trả về bất kỳ đối tượng nào được mô tả bởi đơn vị định dạng đó. Để buộc nó trả về một tuple có kích thước 0 hoặc một, dấu ngoặc đơn của chuỗi định dạng.

Ví dụ [bên trái cuộc gọi, bên phải giá trị python kết quả]:

#define PY_SSIZE_T_CLEAN
#include 
2

1.10. Số lượng tham chiếuReference Counts¶

Trong các ngôn ngữ như C hoặc C ++, lập trình viên chịu trách nhiệm phân bổ động và phân giải bộ nhớ trên đống. Trong C, điều này được thực hiện bằng cách sử dụng các chức năng

PyMODINIT_FUNC
PyInit_spam[void]
{
    PyObject *m;

    m = PyModule_Create[&spammodule];
    if [m == NULL]
        return NULL;

    SpamError = PyErr_NewException["spam.error", NULL, NULL];
    Py_XINCREF[SpamError];
    if [PyModule_AddObject[m, "error", SpamError] >> import spam
>>> status = spam.system["ls -l"]
28 và cuối cùng in tham chiếu mượn. Trông vô hại, phải không? Nhưng nó không phải!

Hãy cùng theo dõi luồng điều khiển vào

#define PY_SSIZE_T_CLEAN
#include 
38. Danh sách sở hữu các tài liệu tham khảo cho tất cả các mục của nó, vì vậy khi mục 1 được thay thế, nó phải xử lý mục gốc 1. Bây giờ hãy giả sử mục gốc 1 là một ví dụ của một lớp do người dùng xác định và giả sử thêm rằng lớp được xác định một phương thức
#define PY_SSIZE_T_CLEAN
#include 
46. Nếu phiên bản lớp này có số lượng tham chiếu là 1, việc xử lý nó sẽ gọi phương thức
#define PY_SSIZE_T_CLEAN
#include 
46 của nó.

Vì nó được viết bằng Python, phương thức

#define PY_SSIZE_T_CLEAN
#include 
46 có thể thực thi mã Python tùy ý. Có lẽ nó có thể làm một cái gì đó để vô hiệu hóa tham chiếu đến
#define PY_SSIZE_T_CLEAN
#include 
49 trong
#define PY_SSIZE_T_CLEAN
#include 
50? Bạn đặt cược! Giả sử rằng danh sách được chuyển vào
#define PY_SSIZE_T_CLEAN
#include 
50 có thể truy cập được vào phương thức
#define PY_SSIZE_T_CLEAN
#include 
46, nó có thể thực hiện một tuyên bố về hiệu ứng của
#define PY_SSIZE_T_CLEAN
#include 
53 và giả sử đây là tài liệu tham khảo cuối cùng cho đối tượng đó, nó sẽ giải phóng bộ nhớ liên quan đến nó, do đó vô hiệu hóa
#define PY_SSIZE_T_CLEAN
#include 
49.

Giải pháp, một khi bạn biết nguồn gốc của vấn đề, thật dễ dàng: tạm thời tăng số lượng tham chiếu. Phiên bản chính xác của hàm có nội dung:

#define PY_SSIZE_T_CLEAN
#include 
4

Đây là một câu chuyện có thật. Một phiên bản cũ của Python chứa các biến thể của lỗi này và ai đó đã dành một khoảng thời gian đáng kể trong trình gỡ lỗi C để tìm ra lý do tại sao các phương thức

#define PY_SSIZE_T_CLEAN
#include 
46 của anh ta sẽ thất bại

Trường hợp thứ hai của các vấn đề với một tham chiếu mượn là một biến thể liên quan đến các chủ đề. Thông thường, nhiều luồng trong phiên dịch viên Python có thể nhận được theo cách khác của nhau, bởi vì có một khóa toàn cầu bảo vệ toàn bộ không gian đối tượng Python. Tuy nhiên, có thể tạm thời giải phóng khóa này bằng macro

#define PY_SSIZE_T_CLEAN
#include 
56 và để lấy lại nó bằng cách sử dụng
#define PY_SSIZE_T_CLEAN
#include 
57. Điều này là phổ biến xung quanh việc chặn các cuộc gọi I/O, để cho các luồng khác sử dụng bộ xử lý trong khi chờ hoàn thành I/O. Rõ ràng, chức năng sau có cùng một vấn đề như trước:

#define PY_SSIZE_T_CLEAN
#include 
5

1.10.4. Null Pointers¶NULL Pointers¶

Nói chung, các chức năng lấy các tài liệu tham khảo đối tượng là đối số không mong đợi bạn vượt qua chúng

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    if [sts  status = spam.system["ls -l"]
38. Không cần thiết phải gửi các tệp tiêu đề Python trong
#define PY_SSIZE_T_CLEAN
#include 
77 - họ đã sử dụng biểu mẫu này nếu ký hiệu
#define PY_SSIZE_T_CLEAN
#include 
78 được xác định [tất cả các trình biên dịch C ++ gần đây xác định ký hiệu này].

1.12. Cung cấp API C cho mô -đun mở rộngProviding a C API for an Extension Module¶

Nhiều mô -đun mở rộng chỉ cung cấp các chức năng và loại mới được sử dụng từ Python, nhưng đôi khi mã trong một mô -đun mở rộng có thể hữu ích cho các mô -đun mở rộng khác. Ví dụ, một mô -đun mở rộng có thể triển khai một loại bộ sưu tập loại hình hoạt động như danh sách mà không cần đặt hàng. Giống như loại danh sách Python tiêu chuẩn có API C cho phép các mô -đun mở rộng để tạo và thao tác danh sách, loại bộ sưu tập mới này sẽ có một bộ hàm C để thao tác trực tiếp từ các mô -đun mở rộng khác.

Ngay từ cái nhìn đầu tiên, điều này có vẻ dễ dàng: Chỉ cần viết các chức năng [tất nhiên không khai báo chúng

>>> import spam
>>> status = spam.system["ls -l"]
36], cung cấp một tệp tiêu đề phù hợp và ghi lại API C. Và trên thực tế, điều này sẽ hoạt động nếu tất cả các mô -đun mở rộng luôn được liên kết tĩnh với trình thông dịch Python. Tuy nhiên, khi các mô -đun được sử dụng làm thư viện được chia sẻ, các ký hiệu được xác định trong một mô -đun có thể không hiển thị cho mô -đun khác. Các chi tiết về khả năng hiển thị phụ thuộc vào hệ điều hành; Một số hệ thống sử dụng một không gian tên toàn cầu cho trình thông dịch Python và tất cả các mô -đun mở rộng [ví dụ, Windows], trong khi các mô -đun khác yêu cầu một danh sách rõ ràng các ký hiệu được nhập tại thời gian liên kết mô -đun [AIX là một ví dụ] hoặc đưa ra lựa chọn các chiến lược khác nhau [hầu hết Đơn vị]. Và ngay cả khi các biểu tượng được hiển thị trên toàn cầu, mô -đun có chức năng mà người ta muốn gọi có thể chưa được tải!

Do đó, tính di động yêu cầu không đưa ra bất kỳ giả định nào về khả năng hiển thị biểu tượng. Điều này có nghĩa là tất cả các ký hiệu trong các mô -đun mở rộng phải được khai báo

>>> import spam
>>> status = spam.system["ls -l"]
36, ngoại trừ chức năng khởi tạo mô -đun, để tránh các cuộc đụng độ tên với các mô -đun mở rộng khác [như được thảo luận trong phần Bảng phương thức và chức năng khởi tạo phương thức mô -đun]. Và nó có nghĩa là các biểu tượng nên có thể truy cập từ các mô -đun mở rộng khác phải được xuất theo một cách khác.The Module’s Method Table and Initialization Function]. And it means that symbols that should be accessible from other extension modules must be exported in a different way.

Python cung cấp một cơ chế đặc biệt để truyền thông tin cấp C [con trỏ] từ mô-đun mở rộng này sang mô-đun mở rộng khác: viên nang. Một viên nang là một loại dữ liệu Python lưu trữ một con trỏ [

#define PY_SSIZE_T_CLEAN
#include 
81]. Viên nang chỉ có thể được tạo và truy cập thông qua API C của chúng, nhưng chúng có thể được truyền xung quanh như bất kỳ đối tượng Python nào khác. Cụ thể, chúng có thể được gán cho một tên trong một không gian tên mô -đun mở rộng. Các mô -đun mở rộng khác sau đó có thể nhập mô -đun này, lấy giá trị của tên này và sau đó lấy con trỏ từ viên nang.

Có nhiều cách trong đó viên nang có thể được sử dụng để xuất API C của mô -đun mở rộng. Mỗi chức năng có thể có được viên nang của riêng mình hoặc tất cả các con trỏ API C có thể được lưu trữ trong một mảng có địa chỉ được xuất bản trong một viên nang. Và các nhiệm vụ khác nhau của việc lưu trữ và truy xuất các con trỏ có thể được phân phối theo những cách khác nhau giữa mô -đun cung cấp mã và mô -đun máy khách.

Dù bạn chọn phương pháp nào, điều quan trọng là đặt tên cho viên nang của bạn đúng cách. Hàm

#define PY_SSIZE_T_CLEAN
#include 
82 có tham số tên [
#define PY_SSIZE_T_CLEAN
#include 
83]; Bạn được phép vượt qua trong một tên
static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    if [sts >> import spam
>>> status = spam.system["ls -l"]
18 trở thành giá trị của một viên nang. Tệp tiêu đề tương ứng với mô -đun cung cấp một macro chăm sóc việc nhập mô -đun và truy xuất các con trỏ API C của nó; Các mô -đun máy khách chỉ phải gọi macro này trước khi truy cập API C.

Mô -đun xuất là một sửa đổi của mô -đun

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
6 từ phần một ví dụ đơn giản. Hàm
>>> import spam
>>> status = spam.system["ls -l"]
15 không gọi trực tiếp chức năng thư viện C
static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
7, mà là một hàm
#define PY_SSIZE_T_CLEAN
#include 
90, tất nhiên sẽ làm một điều gì đó phức tạp hơn trong thực tế [chẳng hạn như thêm vào thư rác vào mỗi lệnh]. Hàm này
#define PY_SSIZE_T_CLEAN
#include 
90 cũng được xuất sang các mô -đun mở rộng khác.A Simple Example. The function
>>> import spam
>>> status = spam.system["ls -l"]
15 does not call the C library function
static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
7 directly, but a function
#define PY_SSIZE_T_CLEAN
#include 
90, which would of course do something more complicated in reality [such as adding “spam” to every command]. This function
#define PY_SSIZE_T_CLEAN
#include 
90 is also exported to other extension modules.

Hàm

#define PY_SSIZE_T_CLEAN
#include 
90 là hàm C đơn giản, được khai báo
>>> import spam
>>> status = spam.system["ls -l"]
36 giống như mọi thứ khác:

#define PY_SSIZE_T_CLEAN
#include 
6

Hàm

>>> import spam
>>> status = spam.system["ls -l"]
24 được sửa đổi theo cách tầm thường:

#define PY_SSIZE_T_CLEAN
#include 
7

Khi bắt đầu mô -đun, ngay sau dòng

Thêm hai dòng phải được thêm vào:

#define PY_SSIZE_T_CLEAN
#include 
8

#define PY_SSIZE_T_CLEAN
#include 
95 được sử dụng để nói với tệp tiêu đề rằng nó đang được đưa vào mô -đun xuất, không phải mô -đun máy khách. Cuối cùng, chức năng khởi tạo mô -đun phải đảm nhiệm việc khởi tạo mảng con trỏ API C:

#define PY_SSIZE_T_CLEAN
#include 
9

Lưu ý rằng

#define PY_SSIZE_T_CLEAN
#include 
96 được khai báo
>>> import spam
>>> status = spam.system["ls -l"]
36; Nếu không, mảng con trỏ sẽ biến mất khi
Py_INCREF[Py_None];
return Py_None;
8 chấm dứt!

Phần lớn công việc nằm trong tệp tiêu đề

#define PY_SSIZE_T_CLEAN
#include 
99, trông như thế này:

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
0

Tất cả những gì một mô -đun máy khách phải làm để có quyền truy cập vào chức năng

#define PY_SSIZE_T_CLEAN
#include 
90 là gọi hàm [hoặc đúng hơn là macro]
static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
01 trong chức năng khởi tạo của nó:

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
1

Nhược điểm chính của phương pháp này là tệp

#define PY_SSIZE_T_CLEAN
#include 
99 khá phức tạp. Tuy nhiên, cấu trúc cơ bản là giống nhau cho mỗi hàm được xuất, do đó nó chỉ được học một lần.

Cuối cùng, cần phải đề cập rằng viên nang cung cấp chức năng bổ sung, đặc biệt hữu ích cho việc phân bổ bộ nhớ và phân giải con trỏ được lưu trữ trong một viên nang. Các chi tiết được mô tả trong Hướng dẫn tham khảo API Python/C trong phần Viên nang và trong việc thực hiện viên nang [Tệp

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
03 và
static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
04 trong phân phối mã nguồn Python].Capsules and in the implementation of Capsules [files
static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
03 and
static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
04 in the Python source code distribution].

Chú thích

1

Một giao diện cho chức năng này đã tồn tại trong mô -đun tiêu chuẩn

static PyObject *
spam_system[PyObject *self, PyObject *args]
{
    const char *command;
    int sts;

    if [!PyArg_ParseTuple[args, "s", &command]]
        return NULL;
    sts = system[command];
    return PyLong_FromLong[sts];
}
05 - nó được chọn là một ví dụ đơn giản và đơn giản.

2

Phép ẩn dụ của việc vay mượn, một tham chiếu là không hoàn toàn chính xác: chủ sở hữu vẫn có một bản sao của tài liệu tham khảo.

3

Kiểm tra xem số lượng tham chiếu ít nhất là không hoạt động - bản thân số lượng tham chiếu có thể nằm trong bộ nhớ được giải phóng và do đó có thể được sử dụng lại cho một đối tượng khác!does not work — the reference count itself could be in freed memory and may thus be reused for another object!

4

Những đảm bảo này don lồng giữ khi bạn sử dụng quy ước gọi kiểu cũ của Old Old - điều này vẫn được tìm thấy trong nhiều mã hiện có.

Có phải tất cả các thư viện Python được viết bằng C?

Hầu hết các thư viện Python được viết bằng ngôn ngữ lập trình C..

Các mô -đun Python được viết trong là gì?

Một mô-đun có thể được viết bằng chính Python, nhưng các mô-đun cũng có thể được viết bằng C và sau đó được tải động trong thời gian chạy.Python itself, but modules can also be written in C and then loaded dynamically at run-time.

Tại sao các thư viện Python được viết bằng C?

Viết bằng ngôn ngữ cấp thấp hơn như C cũng cho phép sử dụng hiệu quả bộ nhớ và thậm chí phát hành khóa phiên dịch toàn cầu để khai thác sự đa dạng đa lõi.Các mô -đun khoa học như Numpy, SCIPY là những ví dụ về những điều này.allows for efficient usage of the memory and even release the global interpreter lock in order to exploit multicore parellilism. Scientific modules like numpy , scipy are examples of these.

Python có được xây dựng trên C ++ không?

Python được viết bằng C [thực tế là triển khai mặc định được gọi là cpython]. [actually the default implementation is called CPython].

Bài Viết Liên Quan

Chủ Đề