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

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

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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
0,
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
1,
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
2 và
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
3. Nếu tệp tiêu đề sau không tồn tại trên hệ thống của bạn, nó sẽ khai báo trực tiếp 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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
4,
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
5 và
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
6.

Điều tiếp theo chúng tôi thêm vào tệp mô -đun của chúng tôi là hàm C sẽ được gọi khi biểu thức Python

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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
7 được đánh giá (chúng tôi sẽ thấy sớm nó kết thúc như thế nào):

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);
}

Có một bản dịch đơn giản từ danh sách đối số trong Python (ví dụ: biểu thức đơ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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
8) cho các đối số được chuyển cho hàm C. Hàm C luôn có hai đối số, theo quy ước được đặt tên là bản thân và args.

Đối số tự chỉ vào đối tượng mô-đun cho các hàm cấp mô-đun; Đối với một phương thức, nó sẽ chỉ vào thể hiện đối tượng.

Đối số ARGS sẽ là một con trỏ tới đối tượng Python Tuple chứa các đối số. Mỗi mục của tuple tương ứng với một đối số trong danh sách đối số cuộc gọi. Các đối số là các đối tượng Python - để làm bất cứ điều gì với chúng trong hàm C của chúng ta, chúng ta phải chuyển đổi chúng thành các giá trị C. 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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9 trong API Python kiểm tra các loại đối số và chuyển đổi chúng thành các giá trị C. Nó sử dụng một chuỗi mẫu để xác định các loại yêu cầu của các đối số cũng như các loại biến C để lưu trữ các giá trị được chuyển đổi. Thêm về điều này sau.

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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9 trả về true (khác không) nếu tất cả các đối số có đúng loại và các thành phần của nó đã được lưu trữ trong các biến có địa chỉ được truyền. Nó trả về false (không) nếu một danh sách đối số không hợp lệ được thông qua. Trong trường hợp sau, nó cũng tăng một ngoại lệ thích hợp để chức năng gọi có thể 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 ngay lập tức (như chúng ta đã thấy trong ví dụ).

1.2. Intermezzo: Lỗi và ngoại lệIntermezzo: Errors and Exceptions¶

Một quy ước quan trọng trong suốt trình thông dịch Python là như sau: khi hàm bị lỗi, nó sẽ đặt một điều kiện ngoại lệ và trả về giá trị lỗi (thường 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
2 hoặ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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1). Thông tin ngoại lệ được lưu trữ trong ba thành viên của trạng thái chủ đề phiên dịch. Đây 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 nếu không có ngoại lệ. Nếu không, chúng là tương đương C của các thành viên của tuple Python được trả lại bởi
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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
5. Đây là loại ngoại lệ, thể hiện ngoại lệ và đối tượng Traceback. Điều quan trọng là phải biết về họ để hiểu cách các lỗi được thông qua xung quanh.

API Python xác định một số chức năng để đặt các loại ngoại lệ khác nhau.

Cái phổ biến nhất 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
6. Đối số của nó là một đối tượng ngoại lệ và chuỗi C. Đối tượng ngoại lệ thường là một đối tượng được xác định trước như
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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
7. Chuỗi C cho biết nguyên nhân của lỗi và được chuyển đổi thành đối tượng chuỗi python và được lưu trữ dưới dạng giá trị liên quan đến của ngoại lệ.

Một chức năng hữu ích khác 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
8, chỉ có một đối số ngoại lệ và xây dựng giá trị liên quan bằng cách kiểm tra biến toàn cầ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);
    if (sts < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
9. Hàm chung nhất là
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0, có hai đối số đối tượng, ngoại lệ và giá trị liên quan của nó. Bạn không cần phải
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
1 Các đối tượng được truyền cho bất kỳ chức năng nào trong số này.

Bạn có thể kiểm tra không phá hủy liệu một ngoại lệ đã được đặt với

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2. Điều này trả về đối tượng ngoại lệ hiện tại hoặ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);
    if (sts < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 nếu không có ngoại lệ nào xảy ra. Bạn thường không cần phải gọi
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 để xem liệu có lỗi xảy ra trong một cuộc gọi chức năng hay không, vì bạn sẽ có thể nói từ giá trị trả về.

Khi một hàm F gọi một hàm khác G phát hiện ra rằng sau đó không thành công, F chính nó sẽ tự trả về giá trị lỗi (thường 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 hoặ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);
    if (sts < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
2). Nó không nên gọi một trong các hàm
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
7 - một đã được G đã được gọi. Sau đó, người gọi F, cũng được cho là sẽ trả về một dấu hiệu lỗi cho người gọi của mình, một lần nữa mà không gọi
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
7, v.v. - nguyên nhân chi tiết nhất của lỗi đã được báo cáo bởi chức năng lần đầu tiên phát hiện nó. Khi lỗi đạt đến vòng lặp chính của trình thông dịch Python, điều này phá hủy mã Python hiện đang thực hiện và cố gắng tìm một trình xử lý ngoại lệ được chỉ định bởi lập trình viên Python.

. Nguyên nhân của lỗi bị mất: Hầu hết các hoạt động có thể thất bại vì nhiều lý do.)

Để bỏ qua một ngoại lệ được đặt bởi một cuộc gọi chức năng không thành công, điều kiện ngoại lệ phải được xóa rõ ràng bằng cách gọi

return PyLong_FromLong(sts);
0. Mã C duy nhất nên gọi
return PyLong_FromLong(sts);
0 là nếu nó không muốn chuyển lỗi cho trình thông dịch nhưng muốn tự xử lý nó hoàn toàn (có thể bằng cách thử một cái gì đó khác, hoặc giả vờ không có gì sai).

Mỗi cuộc gọi

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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
4 thất bại phải được biến thành một ngoại lệ - người gọi trực tiếp 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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
4 (hoặc
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
6) phải gọi
return PyLong_FromLong(sts);
5 và trả về chính chỉ báo lỗi. Tất cả các chức năng tạo đối tượng (ví dụ:
return PyLong_FromLong(sts);
6) đã thực hiện điều này, vì vậy ghi chú này chỉ liên quan đến những người gọi trực tiếp
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
4.

Cũng lưu ý rằng, ngoại trừ quan trọng là

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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9 và bạn bè, các chức năng trả về trạng thái số nguyên thường trả về giá trị dương hoặc không thành công 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
2 cho thất bại, như các cuộc gọi hệ thống UNIX.

Cuối cùng, hãy cẩn thận để làm sạch rác (bằng cách thực hiện các cuộc gọi

Py_INCREF(Py_None);
return Py_None;
0 hoặc
Py_INCREF(Py_None);
return Py_None;
1 cho các đối tượng bạn đã tạo) khi bạn trả về một chỉ báo lỗi!

Sự lựa chọn ngoại lệ nào để nâng cao là hoàn toàn của bạn. Có các đối tượng C trước tương ứng với tất cả các ngoại lệ Python tích hợp, chẳng hạn như

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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
7, mà bạn có thể sử dụng trực tiếp. Tất nhiên, bạn nên chọn các ngoại lệ một cách khôn ngoan - don lồng sử dụng
Py_INCREF(Py_None);
return Py_None;
3 để có nghĩa là một tệp không thể mở ra (điều đó có lẽ là
Py_INCREF(Py_None);
return Py_None;
4). Nếu có gì đó sai với danh sách đối số, 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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9 thường tăng
Py_INCREF(Py_None);
return Py_None;
3. Nếu bạn có một đối số có giá trị phải nằm trong một phạm vi cụ thể hoặc phải đáp ứng các điều kiện khác,
Py_INCREF(Py_None);
return Py_None;
7 là phù hợp.

Bạn cũng có thể xác định một ngoại lệ mới là duy nhất cho mô -đun của bạn. Đối với điều này, bạn thường khai báo một biến đối tượng tĩnh ở đầu tệp của bạn:

static PyObject *SpamError;

và khởi tạo nó trong chức năng khởi tạo mô -đun của bạn (

Py_INCREF(Py_None);
return Py_None;
8) với một đối tượng ngoại lệ:

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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

Lưu ý rằng tên Python cho đối tượng ngoại lệ là

Py_INCREF(Py_None);
return Py_None;
9. Hàm
>>> import spam
>>> status = spam.system("ls -l")
00 có thể tạo một lớp với lớp cơ sở là
>>> import spam
>>> status = spam.system("ls -l")
01 (trừ khi một lớp khác được truyền thay 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1), được mô tả trong các ngoại lệ tích hợp.Built-in Exceptions.

Cũng lưu ý rằng biến

>>> import spam
>>> status = spam.system("ls -l")
03 vẫn giữ một tham chiếu đến lớp ngoại lệ mới được tạo; Đây là cố ý! Vì ngoại lệ có thể được xóa khỏi mô -đun bằng mã bên ngoài, nên cần tham khảo thuộc sở hữu của lớp để đảm bảo rằng nó sẽ không bị loại bỏ, khiến
>>> import spam
>>> status = spam.system("ls -l")
03 trở thành một con trỏ lơ lửng. Nếu nó trở thành một con trỏ lủng lẳng, mã C làm tăng ngoại lệ có thể gây ra một bãi rác lõi hoặc các tác dụng phụ ngoài ý muốn khác.

Chúng tôi thảo luận về việc sử dụng

>>> import spam
>>> status = spam.system("ls -l")
05 như một loại trả về hàm sau này trong mẫu này.

Ngoại lệ

Py_INCREF(Py_None);
return Py_None;
9 có thể được nâng lên trong mô -đun tiện ích mở rộng của bạn bằng cách sử dụng cuộc gọi đế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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
6 như được hiển thị bên dưới:

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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}

1.3. Quay lại ví dụ EnBack to the Example¶

Quay trở lại chức năng ví dụ của chúng tôi, bây giờ bạn sẽ có thể hiểu câu nói này:

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;

Nó 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 (chỉ báo lỗi cho các hàm trả về các con trỏ đối tượng) Nếu phát hiện lỗi trong danh sách đối số, dựa vào ngoại lệ được đặt bởi
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9. Mặt khác, giá trị chuỗi của đối số đã được sao chép vào biến cục bộ
>>> import spam
>>> status = spam.system("ls -l")
10. Đây là một gán con trỏ và bạn không được phép sửa đổi chuỗi mà nó trỏ (vì vậy trong C tiêu chuẩn, biến
>>> 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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9:

Hàm

>>> 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1, có nghĩa là lỗi Lỗi trong hầu hết các bối cảnh, như chúng ta đã thấy.

1.4. Bảng phương thức mô -đun và chức năng khởi tạoThe Module’s Method Table and Initialization Function¶

Tôi đã hứa sẽ chỉ ra cách

>>> 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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9 được sử dụng.

Khi chỉ sử dụng

>>> 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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9; Thông tin thêm về chức năng này được cung cấp dưới đây.

Bit

>>> 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 nếu mô -đun không thể được khởi tạo một cách thỏa đáng. Hàm init phải trả về đối tượng mô -đun cho người gọi của nó, để sau đó nó được chèn vào
>>> 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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9 và các đối số của nó được ghi lại trong phần trích xuất các tham số trong các hàm mở rộng.The Module’s Method Table and Initialization Function. The
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9 function and its arguments are documented in section Extracting Parameters in Extension Functions.

Các macro

>>> 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 (nhưng lưu ý rằng temp sẽ 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 trong bối cảnh này). Thông tin thêm về chúng trong phần tham chiếu phần.Reference Counts.

Sau đó, khi đến lúc gọi hàm, bạn gọi hàm C

>>> 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 hoặc một tuple trống; Để gọi nó bằng một cuộc tranh luận, hãy vượt qua một tuple singleton.
>>> 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1. Nếu có, chức năng Python chấm dứt bằng cách tăng một ngoại lệ. Nếu mã C được gọi là
>>> 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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9. Tham số KWDICT là từ điển của các từ khóa nhận được là tham số thứ ba từ thời gian chạy Python. Tham số KWList là danh sách các chuỗi được kết thúc ____ 61 xác định các tham số; Các tên được khớp với thông tin loại từ định dạng từ trái sang phải. Khi thành công,
>>> 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 ([email protected]):@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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9. Nó được tuyên bố như sau:

#define PY_SSIZE_T_CLEAN
#include 
1

Nó nhận ra một tập hợp các đơn vị định dạng tương tự như các đơn vị được công nhận bởi

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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9, nhưng các đối số (là đầu vào của hàm, không phải đầu ra) không được là con trỏ, chỉ là giá trị. Nó trả về một đối tượng Python mới, phù hợp để trở về từ hàm C được gọi là từ Python.

Một sự khác biệt với

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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
9: Mặc dù sau này yêu cầu đối số đầu tiên của nó là một tuple (vì các danh sách đối số Python luôn được thể hiện dưới dạng các bộ dữ liệu bên trong),
>>> 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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
4 và
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
5. Trong C ++, các toán tử
#define PY_SSIZE_T_CLEAN
#include 
01 và
#define PY_SSIZE_T_CLEAN
#include 
02 được sử dụng với ý nghĩa về cơ bản giống nhau và chúng tôi sẽ hạn chế các cuộc thảo luận sau đây cho trường hợp C.

Mỗi khối bộ nhớ được phân bổ với

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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
4 cuối cùng sẽ được trả lại cho nhóm bộ nhớ có sẵn bằng cách chính xác một cuộc gọi đế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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
5. Điều quan trọng là gọi
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
5 vào đúng thời điểm. Nếu một địa chỉ khối bị lãng quên như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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
5 không được gọi cho nó, bộ nhớ mà nó chiếm không thể được sử dụng lại cho đến khi chương trình chấm dứt. Đây được gọi là rò rỉ bộ nhớ. Mặt khác, nếu một chương trình gọi
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
5 cho một khối và sau đó tiếp tục sử dụng khối, nó sẽ tạo ra một cuộc xung đột với việc sử dụng lại khối thông qua một cuộc gọi
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
4 khác. Điều này được gọi là sử dụng bộ nhớ tự do. Nó có những hậu quả tồi tệ tương tự như tham chiếu dữ liệu không được cung cấp - các bãi rác cốt lõi, kết quả sai, sự cố bí ẩn.

Các nguyên nhân phổ biến của rò rỉ bộ nhớ là các đường dẫn bất thường thông qua mã. Chẳng hạn, một hàm có thể phân bổ một khối bộ nhớ, thực hiện một số tính toán và sau đó giải phóng lại khối. Bây giờ một thay đổi trong các yêu cầu cho hàm có thể thêm một thử nghiệm vào tính toán để phát hiện một điều kiện lỗi và có thể trả về sớm từ chức năng. Thật dễ dàng để quên giải phóng khối bộ nhớ được phân bổ khi lấy lối ra sớm này, đặc biệt là khi nó được thêm vào sau đó vào mã. Những rò rỉ như vậy, một khi được giới thiệu, thường không bị phát hiện trong một thời gian dài: thoát lỗi chỉ được thực hiện trong một phần nhỏ của tất cả các cuộc gọi và hầu hết các máy hiện đại đều có nhiều bộ nhớ ảo, do đó, rò rỉ chỉ trở nên rõ ràng trong quá trình chạy dài sử dụng chức năng rò rỉ thường xuyên. Do đó, điều quan trọng là ngăn chặn rò rỉ xảy ra bằng cách có quy ước hoặc chiến lược mã hóa giúp giảm thiểu loại lỗi này.

Vì Python sử dụng 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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
4 và
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
5, nên nó cần một chiến lược để tránh rò rỉ bộ nhớ cũng như sử dụng bộ nhớ tự do. Phương pháp được chọn được gọi là đếm tham chiếu. Nguyên tắc rất đơn giản: mọi đối tượng đều chứa một bộ đếm, được tăng lên khi tham chiếu đến đối tượng được lưu trữ ở đâu đó và bị giảm khi một tham chiếu đến nó bị xóa. Khi bộ đếm đạt đến 0, tham chiếu cuối cùng đến đối tượng đã bị xóa và đối tượng được giải phóng.

Một chiến lược thay thế được gọi là bộ sưu tập rác tự động. . (Một lợi thế khác được tuyên bố là sự cải thiện trong việc sử dụng tốc độ hoặc bộ nhớ - Tuy nhiên, đây không phải là sự thật khó

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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
4 và
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
5 có sẵn - mà tiêu chuẩn C đảm bảo). Có thể một ngày nào đó, một trình thu gom rác tự động đủ di động sẽ có sẵn cho C. Cho đến lúc đó, chúng tôi sẽ phải sống với số lượng tham chiếu.

Mặc dù Python sử dụng triển khai đếm tham chiếu truyền thống, nó cũng cung cấp một máy dò chu kỳ hoạt động để phát hiện các chu kỳ tham chiếu. Điều này cho phép các ứng dụng không lo lắng về việc tạo các tài liệu tham khảo vòng tròn trực tiếp hoặc gián tiếp; Đây là những điểm yếu của bộ sưu tập rác được thực hiện chỉ bằng cách sử dụng đếm tham chiếu. Các chu kỳ tham chiếu bao gồm các đối tượng có chứa (có thể là gián tiếp) các tham chiếu đến chính chúng, để mỗi đối tượng trong chu kỳ có số lượng tham chiếu không khác. Việc triển khai đếm tham chiếu điển hình không thể lấy lại bộ nhớ thuộc về bất kỳ đối tượng nào trong một chu kỳ tham chiếu hoặc được tham chiếu từ các đối tượng trong chu kỳ, mặc dù không có tham chiếu nào thêm về chính chu kỳ.

Máy dò chu kỳ có thể phát hiện các chu kỳ rác và có thể lấy lại chúng. Mô -đun

#define PY_SSIZE_T_CLEAN
#include 
14 hiển thị một cách để chạy máy dò (hàm
#define PY_SSIZE_T_CLEAN
#include 
15), cũng như giao diện cấu hình và khả năng vô hiệu hóa máy dò khi chạy.

1.10.1. Tham khảo Đếm trong Python¶Reference Counting in Python¶

Có hai macro,

#define PY_SSIZE_T_CLEAN
#include 
16 và
#define PY_SSIZE_T_CLEAN
#include 
17, xử lý sự gia tăng và giảm số lượng tham chiếu.
Py_INCREF(Py_None);
return Py_None;
1 cũng giải phóng đối tượng khi số lượng đạt đến 0. Để linh hoạt, nó không gọi trực tiếp
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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
5 - thay vào đó, nó thực hiện một cuộc gọi thông qua một con trỏ hàm trong đối tượng loại đối tượng. Với mục đích này (và các đối tượng khác cũng chứa một con trỏ tới đối tượng loại của nó.

Câu hỏi lớn bây giờ vẫn còn: Khi nào nên sử dụng

#define PY_SSIZE_T_CLEAN
#include 
16 và
#define PY_SSIZE_T_CLEAN
#include 
17? Trước tiên, hãy giới thiệu một số điều khoản. Không ai sở hữu một đối tượng; Tuy nhiên, bạn có thể sở hữu một tham chiếu đến một đối tượng. Số lượng tham chiếu đối tượng hiện được định nghĩa là số lượng tham chiếu sở hữu cho nó. Chủ sở hữu của một tài liệu tham khảo có trách nhiệm gọi
Py_INCREF(Py_None);
return Py_None;
1 khi không còn cần thiết. Quyền sở hữu của một tài liệu tham khảo có thể được chuyển giao. Có ba cách để xử lý một tài liệu tham khảo sở hữu: chuyển nó, lưu trữ nó hoặc gọi
Py_INCREF(Py_None);
return Py_None;
1. Quên việc xử lý một tài liệu tham khảo sở hữu tạo ra rò rỉ bộ nhớ.

Cũng có thể mượn 2 một tham chiếu đến một đối tượng. Người vay của một tham chiếu không nên gọi

Py_INCREF(Py_None);
return Py_None;
1. Người vay không được giữ đối tượng lâu hơn chủ sở hữu mà nó đã được mượn. Sử dụng một tài liệu tham khảo mượn sau khi chủ sở hữu đã xử lý các rủi ro của nó bằng cách sử dụng bộ nhớ tự do và nên tránh hoàn toàn 3.

Ưu điểm của việc vay khi sở hữu một tài liệu tham khảo là bạn không cần phải quan tâm đến việc xử lý tham chiếu trên tất cả các đường dẫn có thể thông qua mã - nói cách khác, với một tài liệu tham khảo mượn, bạn không có nguy cơ bị rò rỉ khi Thoát sớm được thực hiện. Nhược điểm của việc vay việc sở hữu là có một số tình huống tinh tế trong đó trong mã dường như chính xác, một tài liệu tham khảo mượn có thể được sử dụng sau khi chủ sở hữu mà nó đã được mượn trên thực tế đã được xử lý.

Một tài liệu tham khảo mượn có thể được thay đổi thành một tham chiếu sở hữu bằng cách gọi

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
1. Điều này không ảnh hưởng đến trạng thái của chủ sở hữu mà từ đó tham chiếu đã được mượn - nó tạo ra một tài liệu tham khảo mới và mang lại trách nhiệm toàn bộ (chủ sở hữu mới phải xử lý tham chiếu đúng, cũng như chủ sở hữu trước đó).

1.10.2. Quy tắc sở hữu JoOwnership Rules¶

Bất cứ khi nào một tham chiếu đối tượng được chuyển vào hoặc ra khỏi một hàm, nó là một phần của đặc tả giao diện của chức năng cho dù quyền sở hữu có được chuyển với tham chiếu hay không.

Hầu hết các chức năng trả về một tham chiếu đến một đối tượng chuyển quyền sở hữu với tài liệu tham khảo. Cụ thể, tất cả các chức năng có chức năng tạo ra một đối tượng mới, chẳng hạn như

return PyLong_FromLong(sts);
6 và
>>> import spam
>>> status = spam.system("ls -l")
73, chuyển quyền sở hữu cho người nhận. Ngay cả khi đối tượng không thực sự mới, bạn vẫn nhận được quyền sở hữu một tham chiếu mới cho đối tượng đó. Chẳng hạn,
return PyLong_FromLong(sts);
6 duy trì bộ đệm các giá trị phổ biến và có thể trả về một tham chiếu đến một mục được lưu trong bộ nhớ cache.

Nhiều chức năng trích xuất các đối tượng từ các đối tượng khác cũng chuyển quyền sở hữu với tài liệu tham khảo, ví dụ

#define PY_SSIZE_T_CLEAN
#include 
29. Hình ảnh không rõ ràng, tuy nhiên, ở đây, vì một vài thói quen phổ biến là ngoại lệ:
#define PY_SSIZE_T_CLEAN
#include 
30,
#define PY_SSIZE_T_CLEAN
#include 
31,
#define PY_SSIZE_T_CLEAN
#include 
32 và
#define PY_SSIZE_T_CLEAN
#include 
33 Tất cả các tài liệu tham khảo trả về mà bạn mượn từ bộ tu, danh sách hoặc từ điển.

Hàm

#define PY_SSIZE_T_CLEAN
#include 
34 cũng trả về một tham chiếu mượn, mặc dù nó thực sự có thể tạo đối tượng mà nó trả về: điều này là có thể bởi vì một tham chiếu sở hữu cho đối tượng được lưu trữ trong
>>> import spam
>>> status = spam.system("ls -l")
45.

Khi bạn chuyển một tham chiếu đối tượng vào một chức năng khác, nói chung, hàm mượn tham chiếu từ bạn - nếu nó cần lưu trữ nó, nó sẽ sử dụng

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
1 để trở thành chủ sở hữu độc lập. Có chính xác hai ngoại lệ quan trọng đối với quy tắc này:
#define PY_SSIZE_T_CLEAN
#include 
37 và
#define PY_SSIZE_T_CLEAN
#include 
38. Các chức năng này chiếm quyền sở hữu vật phẩm được truyền cho họ - ngay cả khi chúng thất bại! (Lưu ý rằng
#define PY_SSIZE_T_CLEAN
#include 
39 và bạn bè don don tiếp quản quyền sở hữu - họ là người bình thường.

Khi một hàm C được gọi từ Python, nó mượn các tham chiếu đến các đối số của nó từ người gọi. Người gọi sở hữu một tham chiếu đến đối tượng, do đó, thời gian tham chiếu đã mượn được đảm bảo cho đến khi hàm trở lại. Chỉ khi một tài liệu tham khảo mượn như vậy phải được lưu trữ hoặc truyền tải, nó phải được biến thành một tài liệu tham khảo sở hữu bằng cách gọi

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
1.

Tham chiếu đối tượng được trả về từ một hàm C được gọi từ Python phải là một tài liệu tham khảo sở hữu - quyền sở hữu được chuyển từ chức năng sang người gọi của nó.

1.10.3. Băng mỏngThin Ice¶

Có một vài tình huống dường như vô hại khi sử dụng một tài liệu tham khảo mượn có thể dẫn đến các vấn đề. Tất cả đều phải làm với các lời mời ngầm của thông dịch viên, điều này có thể khiến chủ sở hữu của một tham chiếu để xử lý nó.

Trường hợp đầu tiên và quan trọng nhất cần biết là sử dụng

Py_INCREF(Py_None);
return Py_None;
1 trên một đối tượng không liên quan trong khi mượn một tham chiếu đến một mục danh sách. Ví dụ:

#define PY_SSIZE_T_CLEAN
#include 
3

Hàm này đầu tiên mượn tham chiếu đến

#define PY_SSIZE_T_CLEAN
#include 
42, sau đó thay thế
#define PY_SSIZE_T_CLEAN
#include 
43 bằng giá trị
>>> 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 con trỏ và sẽ đổ lõi (hoặc gây ra các kết xuất lõi sau) nếu bạn làm như vậy. Các chức năng trả về các tham chiếu đối tượng thường 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 chỉ để chỉ ra rằng một ngoại lệ đã xảy ra. Lý do không kiểm tra các đối số
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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 là các chức năng thường truyền các đối tượng chúng nhận được cho chức năng khác - nếu mỗi hàm được kiểm tra
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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1, sẽ có rất nhiều thử nghiệm dự phòng và mã sẽ chạy chậm hơn.

Tốt hơn là chỉ cần kiểm tra

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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 tại Nguồn Nguồn: Khi một con trỏ có thể được nhậ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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1, ví dụ, 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) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
4 hoặc từ một hàm có thể gây ra ngoại lệ.

Các macro

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
1 và
Py_INCREF(Py_None);
return Py_None;
1 không kiểm tra 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 - tuy nhiên, các biến thể của chúng
>>> import spam
>>> status = spam.system("ls -l")
67 và
Py_INCREF(Py_None);
return Py_None;
0 làm.

Các macro để kiểm tra một loại đối tượng cụ thể (

#define PY_SSIZE_T_CLEAN
#include 
70) không kiểm tra 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 - một lần nữa, có nhiều mã gọi một số trong số này liên tiếp để kiểm tra một đối tượng so với các loại dự kiến ​​khác nhau và điều này sẽ tạo ra các thử nghiệm dư thừa . Không có biến thể với kiểm tra
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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1.

Cơ chế gọi chức năng C đảm bảo rằng danh sách đối số được truyền cho các hàm C (

#define PY_SSIZE_T_CLEAN
#include 
73 trong các ví dụ) không bao giờ 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 - thực tế nó đảm bảo rằng nó luôn luôn là Tuple 4.

Đó là một lỗi nghiêm trọng khi để 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1 thoát khỏi người dùng Python.

1.11. Viết tiện ích mở rộng trong C ++ ¶Writing Extensions in C++¶

Có thể viết các mô -đun mở rộng trong C ++. Một số hạn chế áp dụng. Nếu chương trình chính (trình thông dịch Python) được biên dịch và liên kết bởi trình biên dịch C, các đối tượng toàn cầu hoặc tĩnh với các hàm tạo không thể được sử dụng. Đây không phải là vấn đề nếu chương trình chính được liên kết bởi trình biên dịch C ++. Các chức năng sẽ được gọi bởi trình thông dịch Python (đặc biệt, các chức năng khởi tạo mô -đun) phải được khai báo bằng cách sử dụng

>>> import spam
>>> 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 < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1, nhưng chúng tôi rất khuyến khích bạn chỉ định một tên. Viên nang được đặt tên đúng cách cung cấp một mức độ an toàn loại thời gian chạy; Không có cách khả thi để nói với một viên nang giấu tên từ người khác.

Cụ thể, các viên nang được sử dụng để phơi bày các API C nên được đặt tên theo quy ước này:

Hàm tiện lợi

#define PY_SSIZE_T_CLEAN
#include 
85 giúp dễ dàng tải API C được cung cấp thông qua viên nang, nhưng chỉ khi tên Capsule, khớp với quy ước này. Hành vi này cung cấp cho người dùng A API một mức độ chắc chắn cao rằng viên nang mà họ tải chứa API C chính xác.

Ví dụ sau đây cho thấy một cách tiếp cận đặt phần lớn gánh nặng cho người viết mô -đun xuất, phù hợp cho các mô -đun thư viện thường được sử dụng. Nó lưu trữ tất cả các con trỏ API C (chỉ một trong ví dụ!) Trong một mảng của con trỏ

>>> 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).