Hướng dẫn is c used in python? - c được sử dụng trong python?

Từ tất cả mọi thứ tôi đã thấy, đó là sự kết hợp của lý do thực tế và lịch sử. Lý do lịch sử (chủ yếu) là Cpython 1.0 đã được phát hành vào năm 1989. Vào thời điểm đó, C vừa mới được tiêu chuẩn hóa gần đây. C ++ gần như không được biết đến và không thể xác định được, bởi vì hầu như không ai có trình biên dịch C ++.

Show

Mặc dù C ++ phổ biến rộng rãi và dễ dàng hơn ngày hôm nay, nhưng vẫn sẽ mất một lượng lớn công việc để viết lại Cpython vào tập hợp con của C tương thích với C ++. Chính nó, công việc đó sẽ cung cấp ít hoặc không có lợi ích thực sự.

Nó giống như bài đăng trên blog của Joel về việc bắt đầu lại và viết hoàn toàn là sai lầm tồi tệ nhất mà công ty phần mềm có thể mắc phải. Tôi sẽ chống lại điều đó bằng cách chỉ vào chuyển đổi của Microsoft từ lõi Windows 3.0 sang lõi Windows NT và chuyển đổi của Apple từ MacOS 9 sang Mac OS/X. Không ai giết chết công ty-nhưng cả hai đều chắc chắn là những dự án lớn, đắt tiền, dài hạn. Cả hai cũng chỉ ra một cái gì đó rất quan trọng để thành công: duy trì cả hai cơ sở mã đủ lâu để (hầu hết) người dùng có thể chuyển sang cơ sở mã mới khi giải trí, dựa trên lợi ích (ít nhất là nhận thức).

Tuy nhiên, đối với một nhóm phát triển, quy mô của Python, loại thay đổi đó khó khăn hơn nhiều. Ngay cả sự thay đổi từ Python 2 thành 3 cũng đã mất khá nhiều công việc và đòi hỏi phải có sự chồng chéo tương tự. Tuy nhiên, ít nhất trong trường hợp đó, có những lợi ích trực tiếp cho các thay đổi, viết lại thành C ++ (tự nó) sẽ không (ít nhất là ngay lập tức) cung cấp.

Linus Torvalds của C ++ đã được đưa lên, vì vậy tôi cũng sẽ đề cập đến điều đó. Không có gì tôi thấy từ Guido chỉ ra rằng anh ấy có những cảm giác mạnh mẽ, tiêu cực đối với C ++. Về điều tồi tệ nhất tôi thấy anh ấy nói là việc dạy C ++ thường là một thảm họa - nhưng anh ấy ngay lập tức nói rằng điều này phần lớn là do các giáo viên không/không biết C ++.

Tôi cũng nghĩ rằng mặc dù có thể chuyển đổi nhiều mã C thành C ++ một cách dễ dàng tương đối, thì việc nhận được nhiều lợi thế thực sự từ C ++ không chỉ cần viết lại nhiều hơn một chút so với thế, mà còn yêu cầu tái giáo dục đáng kể cho hầu hết các nhà phát triển liên quan. Hầu hết C ++ được viết tốt về cơ bản khác với C viết tốt để làm những điều tương tự. Đây không chỉ là vấn đề thay đổi

static PyObject *SpamError;
1 thành
static PyObject *SpamError;
2 và
static PyObject *SpamError;
3 thành
static PyObject *SpamError;
4, bởi bất kỳ sự tưởng tượng nào.

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 *SpamError;
5.

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 *SpamError;
6 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 *SpamError;
7 (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 *SpamError;
9. .

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

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

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 trước khi bao gồ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;
}
4. 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

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 đều có tiền tố 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;
}
8 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;
}
9, 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 *SpamError;
5 bao gồm một vài tệp tiêu đề 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);
    if (sts < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}
1,
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,
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);
}
3 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);
}
4. 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
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,
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 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);
}
7.

Đ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

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 đượ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

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

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0 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.

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0 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ề
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 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à

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
3 hoặc con trỏ
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2). 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à
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 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
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
6. Đâ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à

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
7. Đố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ư
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
8. 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à

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
9, 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
return PyLong_FromLong(sts);
0. Hàm chung nhất là
return PyLong_FromLong(sts);
1, 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
return PyLong_FromLong(sts);
2 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

return PyLong_FromLong(sts);
3. Điều này trả về đối tượng ngoại lệ hiện tại hoặc
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 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
return PyLong_FromLong(sts);
3 để 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à

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 hoặc
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
3). Nó không nên gọi một trong các hàm
return PyLong_FromLong(sts);
8 - 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
return PyLong_FromLong(sts);
8, 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

Py_INCREF(Py_None);
return Py_None;
1. Mã C duy nhất nên gọi
Py_INCREF(Py_None);
return Py_None;
1 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

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 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
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 (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);
}
7) phải gọi
Py_INCREF(Py_None);
return Py_None;
6 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ụ:
Py_INCREF(Py_None);
return Py_None;
7) đã làm đ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
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.

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

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0 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à
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
3 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

static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
1 hoặc
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
2 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ư

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
8, 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
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
4 có nghĩa là một tệp không thể mở ra (điều đó có lẽ là
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
5). Nếu có gì đó sai với danh sách đối số, hàm
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0 thường tăng
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
4. 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,
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
8 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 hàm khởi tạo mô -đun của bạn (

static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
9) 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à

#define PY_SSIZE_T_CLEAN
#include 
00. Hàm
#define PY_SSIZE_T_CLEAN
#include 
01 có thể tạo một lớp với lớp cơ sở là
#define PY_SSIZE_T_CLEAN
#include 
02 (trừ khi một lớp khác được truyền thay vì
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2), được mô tả trong các ngoại lệ tích hợp.Built-in Exceptions.

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

#define PY_SSIZE_T_CLEAN
#include 
04 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 có tham chiếu thuộc sở hữu của lớp để đảm bảo rằng nó sẽ không bị loại bỏ, khiến
#define PY_SSIZE_T_CLEAN
#include 
04 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

#define PY_SSIZE_T_CLEAN
#include 
06 như một loại trả về hàm sau này trong mẫu này.

Ngoại lệ

#define PY_SSIZE_T_CLEAN
#include 
00 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
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
7 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ề

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 (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
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0. Mặt khác, giá trị chuỗi của đối số đã được sao chép vào biến cục bộ
#define PY_SSIZE_T_CLEAN
#include 
11. Đây là một gán con trỏ và bạn không nên sửa đổi chuỗi mà nó trỏ (vì vậy trong C tiêu chuẩn, biến
#define PY_SSIZE_T_CLEAN
#include 
11 phải được khai báo đúng là
#define PY_SSIZE_T_CLEAN
#include 
13).

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

static PyObject *SpamError;
8, chuyển nó vào chuỗi chúng tôi vừa nhận được từ
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0:

Hàm

#define PY_SSIZE_T_CLEAN
#include 
16 của chúng tôi phải trả về giá trị của
#define PY_SSIZE_T_CLEAN
#include 
17 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
Py_INCREF(Py_None);
return Py_None;
7.

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ả lại hàm), hàm Python tương ứng phải trả về

#define PY_SSIZE_T_CLEAN
#include 
19. Bạn cần thành ngữ này để làm như vậy (được thực hiện bởi macro
#define PY_SSIZE_T_CLEAN
#include 
20):void), the corresponding Python function must return
#define PY_SSIZE_T_CLEAN
#include 
19. You need this idiom to do so (which is implemented by the
#define PY_SSIZE_T_CLEAN
#include 
20 macro):

Py_INCREF(Py_None);
return Py_None;

#define PY_SSIZE_T_CLEAN
#include 
21 là tên C cho đối tượng Python đặc biệt
#define PY_SSIZE_T_CLEAN
#include 
19. Nó là một đối tượng Python chính hãng chứ không phải là một con trỏ
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2, 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

#define PY_SSIZE_T_CLEAN
#include 
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:

static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

Lưu ý mục thứ ba (

#define PY_SSIZE_T_CLEAN
#include 
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à
#define PY_SSIZE_T_CLEAN
#include 
25 hoặc
#define PY_SSIZE_T_CLEAN
#include 
27; Giá trị của
#define PY_SSIZE_T_CLEAN
#include 
28 có nghĩa là một biến thể lỗi thời của
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0 được sử dụng.

Khi chỉ sử dụng

#define PY_SSIZE_T_CLEAN
#include 
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
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0; Thông tin thêm về chức năng này được cung cấp dưới đây.

Bit

#define PY_SSIZE_T_CLEAN
#include 
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ố
#define PY_SSIZE_T_CLEAN
#include 
33 thứ ba sẽ là từ điển của các từ khóa. Sử dụng
#define PY_SSIZE_T_CLEAN
#include 
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:

#define PY_SSIZE_T_CLEAN
#include 
0

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

#define PY_SSIZE_T_CLEAN
#include 
35, trong đó tên là tên của mô-đun và phải là mục duy nhất không phải

#define PY_SSIZE_T_CLEAN
#include 
1

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

#define PY_SSIZE_T_CLEAN
#include 
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à
#define PY_SSIZE_T_CLEAN
#include 
38.

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

static PyObject *SpamError;
7 lần đầu tiên,
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
9 được gọi. .
#define PY_SSIZE_T_CLEAN
#include 
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ề
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 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
#define PY_SSIZE_T_CLEAN
#include 
45.

Khi nhúng python, hàm

static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
9 không được gọi tự động trừ khi có một mục nhập trong bảng
#define PY_SSIZE_T_CLEAN
#include 
47. Để thêm mô -đun vào bảng khởi tạo, hãy sử dụng
#define PY_SSIZE_T_CLEAN
#include 
48, tùy chọn theo sau là nhập mô -đun:

#define PY_SSIZE_T_CLEAN
#include 
2

Ghi chú

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

#define PY_SSIZE_T_CLEAN
#include 
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
#define PY_SSIZE_T_CLEAN
#include 
50 mà không cần can thiệp
#define PY_SSIZE_T_CLEAN
#include 
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à

#define PY_SSIZE_T_CLEAN
#include 
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ừ

#define PY_SSIZE_T_CLEAN
#include 
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
#define PY_SSIZE_T_CLEAN
#include 
50 mà không cần can thiệp
#define PY_SSIZE_T_CLEAN
#include 
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à #define PY_SSIZE_T_CLEAN #include 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 *SpamError;
7 của chúng tôi,
#define PY_SSIZE_T_CLEAN
#include 
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ừ
#define PY_SSIZE_T_CLEAN
#include 
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.

1.5. Biên soạn và liên kết

Có hai điều nữa phải làm trước khi bạn có thể sử dụng tiện ích mở rộng mới của mình: biên dịch và liên kết nó với hệ thống Python. Nếu bạn sử dụng tải động, các chi tiết có thể phụ thuộc vào phong cách tải động mà hệ thống của bạn sử dụng; Xem các chương về các mô -đun mở rộng xây dựng (chương trình mở rộng C và C ++) và thông tin bổ sung chỉ liên quan đến việc xây dựng trên các cửa sổ (Chương xây dựng chương C và C ++ trên Windows) để biết thêm thông tin về điều này.make in the toplevel directory. You can also run make in the

#define PY_SSIZE_T_CLEAN
#include 
57 subdirectory, but then you must first rebuild
#define PY_SSIZE_T_CLEAN
#include 
60 there by running ‘make Makefile’. (This is necessary each time you change the
#define PY_SSIZE_T_CLEAN
#include 
61 file.)

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 *SpamError;
9) trong thư mục
#define PY_SSIZE_T_CLEAN
#include 
57 của phân phối nguồn chưa đóng gói, thêm một dòng vào tệp
#define PY_SSIZE_T_CLEAN
#include 
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 #define PY_SSIZE_T_CLEAN #include 57, nhưng trước tiên bạn phải xây dựng lại #define PY_SSIZE_T_CLEAN #include 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 #define PY_SSIZE_T_CLEAN #include 61.)Calling Python Functions from C¶

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ụ::

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 chức năng 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

return PyLong_FromLong(sts);
2!) 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:

#define PY_SSIZE_T_CLEAN
#include 
3

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

#define PY_SSIZE_T_CLEAN
#include 
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
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0 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
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0 function and its arguments are documented in section Extracting Parameters in Extension Functions.

Các macro

#define PY_SSIZE_T_CLEAN
#include 
67 và
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
1 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ỏ
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 (nhưng lưu ý rằng temp sẽ không phải là
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 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

#define PY_SSIZE_T_CLEAN
#include 
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
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 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.
#define PY_SSIZE_T_CLEAN
#include 
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ụ:

#define PY_SSIZE_T_CLEAN
#include 
4

#define PY_SSIZE_T_CLEAN
#include 
71 Trả về một con trỏ đối tượng Python: Đây là giá trị trả về của hàm Python.
#define PY_SSIZE_T_CLEAN
#include 
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ố, là ________ 92-ed ngay sau cuộc gọi
#define PY_SSIZE_T_CLEAN
#include 
71.

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

#define PY_SSIZE_T_CLEAN
#include 
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 đó
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
2 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ả lại không phải là

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2. 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à
#define PY_SSIZE_T_CLEAN
#include 
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
Py_INCREF(Py_None);
return Py_None;
1. Ví dụ:

#define PY_SSIZE_T_CLEAN
#include 
5

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

#define PY_SSIZE_T_CLEAN
#include 
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
#define PY_SSIZE_T_CLEAN
#include 
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:

#define PY_SSIZE_T_CLEAN
#include 
6

Lưu ý vị trí của

#define PY_SSIZE_T_CLEAN
#include 
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:
#define PY_SSIZE_T_CLEAN
#include 
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

#define PY_SSIZE_T_CLEAN
#include 
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
#define PY_SSIZE_T_CLEAN
#include 
73 để xây dựng từ điển.

#define PY_SSIZE_T_CLEAN
#include 
7

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

Hàm

#define PY_SSIZE_T_CLEAN
#include 
34 được khai báo như sau:

#define PY_SSIZE_T_CLEAN
#include 
8

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

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0. 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 ____ 62 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,
#define PY_SSIZE_T_CLEAN
#include 
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

#define PY_SSIZE_T_CLEAN
#include 
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 
9

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

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0. Nó được tuyên bố như sau:

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

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

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0, 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

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
0: 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),
#define PY_SSIZE_T_CLEAN
#include 
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ề
#define PY_SSIZE_T_CLEAN
#include 
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ả):

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

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

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 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);
}
6. Trong C ++, các toán tử
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 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);
}
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

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 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
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ều quan trọng là gọ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);
}
6 vào đúng thời điểm. Nếu một địa chỉ khối bị lãng quên như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);
}
6 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
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 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
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 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

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 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);
}
6, 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ó

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

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);
}
14 hiển thị một cách để chạy máy dò (hàm
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);
}
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,

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);
}
16 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);
}
17, xử lý sự gia tăng và giảm số lượng tham chiếu.
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
2 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
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 - 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

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);
}
16 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);
}
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
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
2 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
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
2. 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

static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
2. 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

return PyLong_FromLong(sts);
2. Đ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ư

Py_INCREF(Py_None);
return Py_None;
7 và
#define PY_SSIZE_T_CLEAN
#include 
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,
Py_INCREF(Py_None);
return Py_None;
7 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ụ

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);
}
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ệ:
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);
}
30,
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);
}
31,
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);
}
32 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);
}
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

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);
}
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
#define PY_SSIZE_T_CLEAN
#include 
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

return PyLong_FromLong(sts);
2 để 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:
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);
}
37 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);
}
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
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);
}
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

return PyLong_FromLong(sts);
2.

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

static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
2 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ụ:

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

Hàm này đầu tiên mượn tham chiếu đế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);
}
42, sau đó thay thế
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);
}
43 bằng giá trị
#define PY_SSIZE_T_CLEAN
#include 
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

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);
}
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
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);
}
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
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);
}
46 của nó.

Vì nó được viết bằng Python, phương thứ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);
}
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
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);
}
49 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);
    return PyLong_FromLong(sts);
}
50? Bạn đặt cược! Giả sử rằng danh sách được chuyển và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);
}
50 có thể truy cập được vào phương thứ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);
}
46, nó có thể thực hiện một tuyên bố về hiệu ứng của
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);
}
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
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);
}
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:

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

Đâ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

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

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);
}
56 và để lấy lại nó bằng cách sử dụ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);
    return PyLong_FromLong(sts);
}
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:

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

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

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 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ề
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 chỉ để chỉ ra rằng một ngoại lệ đã xảy ra. Lý do không kiểm tra đối số
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 là các hàm 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
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2, 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

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 tại Nguồn Nguồn: Từ khi một con trỏ có thể nhận được
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2, ví dụ, từ
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 hoặc từ một hàm có thể gây ra ngoại lệ.

Các macro

return PyLong_FromLong(sts);
2 và
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
2 không kiểm tra các con trỏ
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 - tuy nhiên, các biến thể của chúng
#define PY_SSIZE_T_CLEAN
#include 
67 và
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
1 làm.

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

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);
}
70) không kiểm tra các con trỏ
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 - 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
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2.

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 (

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);
}
73 trong các ví dụ) không bao giờ là
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 - thực tế nó đảm bảo rằng nó luôn luôn là một Tuple 4.

Đó là một lỗi nghiêm trọng khi để một con trỏ

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 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

#define PY_SSIZE_T_CLEAN
#include 
38. Không cần thiết phải gửi các tệp tiêu đề Python 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);
    return PyLong_FromLong(sts);
}
77 - họ đã sử dụng biểu mẫu này nếu ký hiệ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);
}
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

#define PY_SSIZE_T_CLEAN
#include 
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

#define PY_SSIZE_T_CLEAN
#include 
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ỏ (void*). 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.void*). Capsules can only be created and accessed via their C API, but they can be passed around like any other Python object. In particular, they can be assigned to a name in an extension module’s namespace. Other extension modules can then import this module, retrieve the value of this name, and then retrieve the pointer from the Capsule.

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

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);
}
81 có tham số tên (Constchar*); Bạn được phép vượt qua trong một tên
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2, 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.constchar*); you’re permitted to pass in a
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
2 name, but we strongly encourage you to specify a name. Properly named Capsules provide a degree of runtime type-safety; there is no feasible way to tell one unnamed Capsule from another.

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

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);
}
83 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 loạt các con trỏ void 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.void pointers which becomes the value of a Capsule. The header file corresponding to the module provides a macro that takes care of importing the module and retrieving its C API pointers; client modules only have to call this macro before accessing the C API.

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

static PyObject *SpamError;
7 từ phần một ví dụ đơn giản. Hàm
#define PY_SSIZE_T_CLEAN
#include 
16 không gọi trực tiếp chức năng thư viện C
static PyObject *SpamError;
8, mà là một hàm
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);
}
87, 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
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);
}
87 cũng được xuất sang các mô -đun mở rộng khác.A Simple Example. The function
#define PY_SSIZE_T_CLEAN
#include 
16 does not call the C library function
static PyObject *SpamError;
8 directly, but a 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);
}
87, which would of course do something more complicated in reality (such as adding “spam” to every command). This 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);
}
87 is also exported to other extension modules.

Hàm

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);
}
87 là hàm C đơn giản, được khai báo
#define PY_SSIZE_T_CLEAN
#include 
36 giống như mọi thứ khá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);
}
5

Hàm

#define PY_SSIZE_T_CLEAN
#include 
24 được sửa đổi theo cách tầm thườ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);
    return PyLong_FromLong(sts);
}
6

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

Thêm hai dòng phải được thêm và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);
}
7

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);
}
92 đượ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:

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

Lưu ý rằ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);
    return PyLong_FromLong(sts);
}
93 được khai báo
#define PY_SSIZE_T_CLEAN
#include 
36; Nếu không, mảng con trỏ sẽ biến mất khi
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
9 chấm dứt!

Phần lớn công việc nằm trong tệp 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);
}
96, 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);
}
9

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

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);
}
87 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);
}
98 trong chức năng khởi tạo của nó:

static PyObject *SpamError;
0

Nhược điểm chính của phương pháp này là 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);
}
96 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 *SpamError;
00 và
static PyObject *SpamError;
01 trong phân phối mã nguồn Python).Capsules and in the implementation of Capsules (files
static PyObject *SpamError;
00 and
static PyObject *SpamError;
01 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 *SpamError;
02 - 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ó.

Tại sao C được sử dụng trên Python?

Python là một ngôn ngữ đa năng được sử dụng để học máy, xử lý ngôn ngữ tự nhiên, phát triển web và nhiều thứ khác.C chủ yếu được sử dụng để phát triển ứng dụng liên quan đến phần cứng như hệ điều hành, trình điều khiển mạng.C is mainly used for hardware-related application development such as operating systems, network drivers.

C có cần phải học Python không?

Nếu bạn có kiến thức trước về C trước khi học Python, nó sẽ giúp bạn hiểu mọi thứ rất dễ dàng.Nhưng nếu không biết bất kỳ C hoặc C ++ nào, nó vẫn ổn.Python và C hoặc C ++ là độc lập.Vì vậy, nếu bạn không biết C hoặc C ++, không có vấn đề gì chỉ cần tiếp tục và khám phá Python.. But if don't know any of those c or c++ , it's fine . Python and c or c++ are independent. So If you don't know c or c++, no problem at all just go ahead and explore Python.

Python có thể tương tác với C không?

Nói chung, mã C đã viết sẽ không yêu cầu sửa đổi được sử dụng bởi Python.Công việc duy nhất chúng ta cần làm để tích hợp mã C vào Python là về phía Python.Các bước để giao tiếp với Python với C bằng cách sử dụng CTYPE.already-written C code will require no modifications to be used by Python. The only work we need to do to integrate C code in Python is on Python's side. The steps for interfacing Python with C using Ctypes.