Mở rộng/Nhúng FAQ¶
Tôi có thể tạo các hàm của riêng mình trong C không?¶
Có, bạn có thể tạo các mô-đun tích hợp chứa các hàm, biến, ngoại lệ và thậm chí cả các kiểu mới trong C. Điều này được giải thích trong tài liệu Mở rộng và nhúng trình thông dịch Python.
Hầu hết các sách Python ở trình độ trung cấp hoặc nâng cao cũng sẽ đề cập đến chủ đề này.
Tôi có thể tạo các hàm của riêng mình trong C++ không?¶
Có, sử dụng các tính năng tương thích với C có trong C++. Đặt extern "C" { ... } xung quanh các tệp bao gồm Python và đặt extern "C" trước mỗi hàm sẽ được trình thông dịch Python gọi. Các đối tượng C++ toàn cục hoặc tĩnh với các hàm tạo có lẽ không phải là một ý tưởng hay.
Viết C rất khó; có lựa chọn thay thế nào không?¶
Có một số lựa chọn thay thế để viết phần mở rộng C của riêng bạn, tùy thuộc vào việc bạn đang cố gắng thực hiện. Recommended third party tools cung cấp cả cách tiếp cận đơn giản hơn và phức tạp hơn để tạo phần mở rộng C và C++ cho Python.
Làm cách nào tôi có thể thực thi các câu lệnh Python tùy ý từ C?¶
Hàm cấp cao nhất để thực hiện việc này là PyRun_SimpleString(), hàm này nhận một đối số chuỗi đơn để thực thi trong ngữ cảnh của mô-đun __main__ và trả về 0 nếu thành công và -1 khi xảy ra ngoại lệ (bao gồm cả SyntaxError). Nếu bạn muốn kiểm soát nhiều hơn, hãy sử dụng PyRun_String(); xem nguồn của PyRun_SimpleString() trong Python/pythonrun.c.
Làm cách nào tôi có thể đánh giá một biểu thức Python tùy ý từ C?¶
Gọi hàm PyRun_String() từ câu hỏi trước bằng ký hiệu bắt đầu Py_eval_input; nó phân tích một biểu thức, đánh giá nó và trả về giá trị của nó.
Làm cách nào để trích xuất các giá trị C từ đối tượng Python?¶
Điều đó phụ thuộc vào loại đối tượng. Nếu đó là một bộ, PyTuple_Size() trả về độ dài của nó và PyTuple_GetItem() trả về mục ở một chỉ mục được chỉ định. Danh sách có chức năng tương tự, PyList_Size() và PyList_GetItem().
Đối với byte, PyBytes_Size() trả về độ dài của nó và PyBytes_AsStringAndSize() cung cấp một con trỏ tới giá trị và độ dài của nó. Lưu ý rằng các đối tượng byte Python có thể chứa byte rỗng nên không nên sử dụng strlen() của C.
Để kiểm tra loại đối tượng, trước tiên hãy đảm bảo rằng đối tượng đó không phải là NULL, sau đó sử dụng PyBytes_Check(), PyTuple_Check(), PyList_Check(), v.v.
Ngoài ra còn có các đối tượng API cấp cao cho Python được cung cấp bởi cái gọi là giao diện 'trừu tượng' - hãy đọc Include/abstract.h để biết thêm chi tiết. Nó cho phép giao tiếp với bất kỳ loại chuỗi Python nào bằng cách sử dụng các lệnh gọi như PySequence_Length(), PySequence_GetItem(), v.v. cũng như nhiều giao thức hữu ích khác như số (PyNumber_Index() và cộng sự) và ánh xạ trong API PyMapping.
Làm cách nào để sử dụng Py_BuildValue() để tạo một bộ có độ dài tùy ý?¶
Bạn không thể. Thay vào đó hãy sử dụng PyTuple_Pack().
Làm cách nào để gọi phương thức của đối tượng từ C?¶
Hàm PyObject_CallMethod() có thể được sử dụng để gọi một phương thức tùy ý của một đối tượng. Các tham số là đối tượng, tên của phương thức cần gọi, chuỗi định dạng giống như chuỗi được sử dụng với Py_BuildValue() và các giá trị đối số:
PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
const char *arg_format, ...);
Điều này hoạt động với bất kỳ đối tượng nào có phương thức -- cho dù được tích hợp sẵn hay do người dùng xác định. Cuối cùng, bạn chịu trách nhiệm về giá trị trả về Py_DECREF()'.
Để gọi, ví dụ: phương thức "tìm kiếm" của đối tượng tệp với các đối số 10, 0 (giả sử con trỏ đối tượng tệp là "f"):
res = PyObject_CallMethod(f, "tìm kiếm", "(ii)", 10, 0);
nếu (res == NULL) {
... một ngoại lệ đã xảy ra ...
}
khác {
Py_DECREF(res);
}
Lưu ý rằng vì PyObject_CallObject() always muốn một bộ cho danh sách đối số, nên để gọi một hàm không có đối số, hãy chuyển "()" cho định dạng và để gọi một hàm có một đối số, hãy đặt đối số trong dấu ngoặc đơn, ví dụ: "(Tôi)".
Làm cách nào để bắt được đầu ra từ PyErr_Print() (hoặc bất cứ thứ gì in ra thiết bị xuất chuẩn/thiết bị xuất chuẩn)?¶
Trong mã Python, xác định một đối tượng hỗ trợ phương thức write(). Gán đối tượng này cho sys.stdout và sys.stderr. Gọi print_error hoặc chỉ cho phép cơ chế truy nguyên tiêu chuẩn hoạt động. Sau đó, đầu ra sẽ đi đến bất cứ nơi nào mà phương thức write() của bạn gửi nó.
Cách dễ nhất để làm điều này là sử dụng lớp io.StringIO:
>>> nhập io, sys
>>> sys.stdout = io.StringIO()
>>> in('foo')
>>> print('xin chào thế giới!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
xin chào thế giới!
Một đối tượng tùy chỉnh để làm điều tương tự sẽ trông như thế này:
>>> nhập io, sys
>>> lớp StdoutCatcher(io.TextIOBase):
... def __init__(self):
... self.data = []
... def write(tự, nội dung):
... self.data.append(thứ)
...
>>> nhập hệ thống
>>> sys.stdout = StdoutCatcher()
>>> in('foo')
>>> print('xin chào thế giới!')
>>> sys.stderr.write(''.join(sys.stdout.data))
foo
xin chào thế giới!
Làm cách nào để truy cập mô-đun được viết bằng Python từ C?¶
Bạn có thể lấy một con trỏ tới đối tượng mô-đun như sau
mô-đun = PyImport_ImportModule("<tên mô-đun>");
Nếu mô-đun chưa được nhập (tức là nó chưa có trong sys.modules), thao tác này sẽ khởi tạo mô-đun; nếu không nó chỉ trả về giá trị của sys.modules["<modulename>"]. Lưu ý rằng nó không đưa mô-đun vào bất kỳ không gian tên nào -- nó chỉ đảm bảo rằng nó đã được khởi tạo và được lưu trữ trong sys.modules.
Sau đó, bạn có thể truy cập các thuộc tính của mô-đun (tức là bất kỳ tên nào được xác định trong mô-đun) như sau:
attr = PyObject_GetAttrString(module, "<attrname>");
Gọi PyObject_SetAttrString() để gán cho các biến trong mô-đun cũng hoạt động.
Làm cách nào để giao tiếp với các đối tượng C++ từ Python?¶
Tùy thuộc vào yêu cầu của bạn, có nhiều cách tiếp cận. Để thực hiện việc này một cách thủ công, hãy bắt đầu bằng cách đọc the "Extending and Embedding" document. Nhận ra rằng đối với hệ thống thời gian chạy Python, không có nhiều sự khác biệt giữa C và C++ -- vì vậy chiến lược xây dựng một kiểu Python mới xung quanh kiểu cấu trúc C (con trỏ) cũng sẽ có tác dụng với các đối tượng C++.
Đối với thư viện C++, xem Viết C rất khó; có lựa chọn thay thế nào không?.
Tôi đã thêm một mô-đun bằng tệp Cài đặt và không thành công; Tại sao?¶
Quá trình thiết lập phải kết thúc bằng dòng mới, nếu không có dòng mới ở đó thì quá trình xây dựng sẽ thất bại. (Việc khắc phục điều này đòi hỏi phải thực hiện một số hành vi tấn công tập lệnh shell xấu xí và lỗi này quá nhỏ nên có vẻ như không đáng nỗ lực.)
Làm cách nào để gỡ lỗi tiện ích mở rộng?¶
Khi sử dụng GDB với các tiện ích mở rộng được tải động, bạn không thể đặt điểm dừng trong tiện ích mở rộng của mình cho đến khi tiện ích mở rộng của bạn được tải.
Trong tệp .gdbinit của bạn (hoặc tương tác), hãy thêm lệnh:
br _PyImport_LoadDynamicModule
Sau đó, khi bạn chạy GDB:
$ gdb/local/bin/python
gdb) chạy myscript.py
gdb) hãy tiếp tục # repeat cho đến khi tiện ích mở rộng của bạn được tải
gdb) kết thúc # so rằng tiện ích mở rộng của bạn đã được tải
gdb) br myfunction.c:50
gdb) tiếp tục
Tôi muốn biên dịch mô-đun Python trên hệ thống Linux của mình nhưng thiếu một số tệp. Tại sao?¶
Hầu hết các phiên bản đóng gói của Python đều bỏ qua một số tệp cần thiết để biên dịch các phần mở rộng Python.
Đối với Red Hat, hãy cài đặt RPM python3-devel để lấy các tệp cần thiết.
Đối với Debian, hãy chạy apt-get install python3-dev.
Làm cách nào để phân biệt "đầu vào không đầy đủ" với "đầu vào không hợp lệ"?¶
Đôi khi, bạn muốn mô phỏng hành vi của trình thông dịch tương tác Python, trong đó nó cung cấp cho bạn lời nhắc tiếp tục khi dữ liệu đầu vào chưa hoàn chỉnh (ví dụ: bạn đã nhập phần đầu của câu lệnh "if" hoặc bạn không đóng dấu ngoặc đơn hoặc dấu ngoặc kép ba chuỗi), nhưng nó sẽ cung cấp cho bạn thông báo lỗi cú pháp ngay lập tức khi dữ liệu đầu vào không hợp lệ.
Trong Python, bạn có thể sử dụng mô-đun codeop, mô-đun này gần đúng với hành vi của trình phân tích cú pháp. IDLE sử dụng điều này chẳng hạn.
Cách dễ nhất để thực hiện điều đó trong C là gọi PyRun_InteractiveLoop() (có thể trong một luồng riêng biệt) và để trình thông dịch Python xử lý dữ liệu đầu vào cho bạn. Bạn cũng có thể đặt PyOS_ReadlineFunctionPointer() trỏ đến chức năng nhập tùy chỉnh của mình. Xem Modules/readline.c và Parser/myreadline.c để biết thêm gợi ý.
Làm cách nào để tìm các ký hiệu g++ không xác định __buildin_new hoặc __pure_virtual?¶
Để tải động các mô-đun mở rộng g++, bạn phải biên dịch lại Python, liên kết lại bằng g++ (thay đổi LINKCC trong Makefile Mô-đun Python) và liên kết mô-đun mở rộng của bạn bằng g++ (ví dụ: g++ -shared -o mymodule.so mymodule.o).
Tôi có thể tạo một lớp đối tượng với một số phương thức được triển khai trong C và các phương thức khác bằng Python (ví dụ: thông qua tính kế thừa) không?¶
Có, bạn có thể kế thừa từ các lớp tích hợp sẵn như int, list, dict, v.v.
Thư viện Boost Python (BPL, https://www.boost.org/libs/python/doc/index.html) cung cấp cách thực hiện việc này từ C++ (tức là bạn có thể kế thừa từ một lớp mở rộng được viết bằng C++ bằng BPL).