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 #include00. Hàm #define PY_SSIZE_T_CLEAN #include01 có thể tạo một lớp với lớp cơ sở là #define PY_SSIZE_T_CLEAN #include02 (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 #include04 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 #include04 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 #include06 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 #include00 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 #include11. Đâ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 #include11 phải được khai báo đúng là #define PY_SSIZE_T_CLEAN #include13). 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 #include16 của chúng tôi phải trả về giá trị của #define PY_SSIZE_T_CLEAN #include17 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 #include19. 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 #include20):void), the corresponding Python function must return #define PY_SSIZE_T_CLEAN #include19. You need this idiom to do so (which is implemented by the #define PY_SSIZE_T_CLEAN #include20 macro): Py_INCREF(Py_None); return Py_None; #define PY_SSIZE_T_CLEAN #include21 là tên C cho đối tượng Python đặc biệt #define PY_SSIZE_T_CLEAN #include19. 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 #include24 đượ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 #include25). Đâ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 #include25 hoặc #define PY_SSIZE_T_CLEAN #include27; Giá trị của #define PY_SSIZE_T_CLEAN #include28 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 #include25, 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 #include32 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 #include33 thứ ba sẽ là từ điển của các từ khóa. Sử dụng #define PY_SSIZE_T_CLEAN #include34 để 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 #include0 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 #include35, 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 #include1 Lưu ý rằng pymodinit_func tuyên bố hàm là loại trả về #define PY_SSIZE_T_CLEAN #include33, 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 #include38. 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 #include41 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 #include45. 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 #include47. Để thêm mô -đun vào bảng khởi tạo, hãy sử dụng #define PY_SSIZE_T_CLEAN #include48, tùy chọn theo sau là nhập mô -đun: #define PY_SSIZE_T_CLEAN #include2 Ghi chú Loại bỏ các mục từ #define PY_SSIZE_T_CLEAN #include45 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 #include50 mà không cần can thiệp #define PY_SSIZE_T_CLEAN #include51) 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 #include52. 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 #include45 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 #include50 mà không cần can thiệp #define PY_SSIZE_T_CLEAN #include51) 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 |