Giới thiệu

Giao diện của Lập trình viên Ứng dụng với Python cung cấp cho các lập trình viên C và C++ quyền truy cập vào trình thông dịch Python ở nhiều cấp độ khác nhau. API đều có thể sử dụng được từ C++, nhưng để cho ngắn gọn, nó thường được gọi là Python/C API. Có hai lý do cơ bản khác nhau để sử dụng Python/C API. Lý do đầu tiên là viết extension modules cho mục đích cụ thể; đây là các mô-đun C mở rộng trình thông dịch Python. Đây có lẽ là cách sử dụng phổ biến nhất. Lý do thứ hai là sử dụng Python làm thành phần trong một ứng dụng lớn hơn; kỹ thuật này thường được gọi là embedding Python trong một ứng dụng.

Viết một mô-đun mở rộng là một quá trình được hiểu tương đối rõ ràng, trong đó cách tiếp cận "sách dạy nấu ăn" hoạt động hiệu quả. Có một số công cụ tự động hóa quá trình ở một mức độ nào đó. Mặc dù mọi người đã nhúng Python vào các ứng dụng khác kể từ khi nó tồn tại nhưng quá trình nhúng Python không đơn giản bằng việc viết một phần mở rộng.

Nhiều hàm API hữu ích bất kể bạn đang nhúng hay mở rộng Python; hơn nữa, hầu hết các ứng dụng nhúng Python cũng sẽ cần cung cấp tiện ích mở rộng tùy chỉnh, vì vậy có lẽ bạn nên làm quen với việc viết tiện ích mở rộng trước khi thử nhúng Python vào một ứng dụng thực.

Khả năng tương thích phiên bản ngôn ngữ

C API của Python tương thích với các phiên bản C11 và C++11 của C và C++.

Đây là giới hạn thấp hơn: C API không yêu cầu các tính năng từ các phiên bản C/C++ mới hơn. Bạn not cần bật "chế độ c11" của trình biên dịch.

Tiêu chuẩn mã hóa

Nếu bạn đang viết mã C để đưa vào CPython, must hãy tuân theo các nguyên tắc và tiêu chuẩn được xác định trong PEP 7. Những nguyên tắc này áp dụng bất kể phiên bản Python mà bạn đang đóng góp. Việc tuân theo các quy ước này là không cần thiết đối với các mô-đun mở rộng của bên thứ ba của riêng bạn, trừ khi cuối cùng bạn muốn đóng góp chúng cho Python.

Bao gồm các tệp

Tất cả các định nghĩa về hàm, loại và macro cần thiết để sử dụng API Python/C đều được bao gồm trong mã của bạn theo dòng sau:

#define PY_SSIZE_T_CLEAN
#include <Python.h>

Điều này ngụ ý bao gồm các tiêu đề tiêu chuẩn sau: <stdio.h>, <string.h>, <errno.h>, <limits.h>, <assert.h><stdlib.h> (nếu có).

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, nên bạn must bao gồm Python.h trước bất kỳ tiêu đề tiêu chuẩn nào được đưa vào.

Bạn nên luôn xác định PY_SSIZE_T_CLEAN trước khi thêm Python.h. Xem Phân tích đối số và xây dựng giá trị để biết mô tả về macro này.

Tất cả tên hiển thị của người dùng được xác định bởi Python.h (ngoại trừ những tên được xác định bởi tiêu đề tiêu chuẩn đi kèm) đều có một trong các tiền tố Py hoặc _Py. Các tên bắt đầu bằng _Py được sử dụng nội bộ khi triển khai Python và người viết tiện ích mở rộng không nên sử dụng. Tên thành viên cấu trúc không có tiền tố dành riêng.

Ghi chú

Mã người dùng không bao giờ được xác định tên bắt đầu bằng Py hoặc _Py. Điều này gây nhầm lẫn cho người đọc và gây nguy hiểm cho tính khả chuyển của mã người dùng sang các phiên bản Python trong tương lai, phiên bản này có thể xác định các tên bổ sung bắt đầu bằng một trong các tiền tố này.

Các tệp tiêu đề thường được cài đặt bằng Python. Trên Unix, chúng nằm trong thư mục prefix/include/pythonversion/exec_prefix/include/pythonversion/, trong đó prefixexec_prefix được xác định bởi các tham số tương ứng với tập lệnh configure của Python và version'%d.%d' % sys.version_info[:2]. Trên Windows, các tiêu đề được cài đặt trong prefix/include, trong đó prefix là thư mục cài đặt được chỉ định cho trình cài đặt.

Để bao gồm các tiêu đề, hãy đặt cả hai thư mục (nếu khác) trên đường dẫn tìm kiếm của trình biên dịch để bao gồm. not có đặt các thư mục mẹ trên đường dẫn tìm kiếm rồi sử dụng #include <pythonX.Y/Python.h>; điều này sẽ phá vỡ các bản dựng đa nền tảng vì các tiêu đề độc lập với nền tảng trong prefix bao gồm các tiêu đề dành riêng cho nền tảng từ exec_prefix.

Người dùng C++ nên lưu ý rằng mặc dù API được xác định hoàn toàn bằng C, các tệp tiêu đề khai báo chính xác các điểm vào là extern "C". Do đó, không cần phải làm gì đặc biệt để sử dụng API từ C++.

Các macro hữu ích

Một số macro hữu ích được xác định trong tệp tiêu đề Python. Nhiều thứ được xác định gần hơn với mức độ hữu ích của chúng (ví dụ: Py_RETURN_NONE, PyMODINIT_FUNC). Những tiện ích khác có tính tổng quát hơn được định nghĩa ở đây. Đây không nhất thiết phải là một danh sách đầy đủ.

Py_CAN_START_THREADS

Nếu macro này được xác định thì hệ thống hiện tại có thể bắt đầu các luồng.

Hiện tại, tất cả các hệ thống được CPython hỗ trợ (theo PEP 11), ngoại trừ một số nền tảng WebAssugging, đều hỗ trợ các luồng bắt đầu.

Added in version 3.13.

Py_GETENV(s)

Giống như getenv(s), nhưng trả về NULL nếu -E được truyền trên dòng lệnh (xem PyConfig.use_environment).

Macro chuỗi tài liệu

PyDoc_STRVAR(name, str)

Tạo một biến có tên name có thể được sử dụng trong chuỗi tài liệu. Nếu Python được xây dựng mà không có chuỗi tài liệu (--without-doc-strings), giá trị sẽ là một chuỗi trống.

Ví dụ:

PyDoc_STRVAR(pop_doc, "Xóa và trả về phần tử ngoài cùng bên phải.");

tĩnh PyMethodDef deque_methods[] = {
    // ...
    {"pop", (PyCFunction)deque_pop, METH_NOARGS, pop_doc},
    // ...
}

Mở rộng tới PyDoc_VAR(name) = PyDoc_STR(str).

PyDoc_STR(str)

Mở rộng thành chuỗi đầu vào đã cho hoặc một chuỗi trống nếu chuỗi tài liệu bị tắt (--without-doc-strings).

Ví dụ:

PyMethodDef tĩnh pysqlite_row_methods[] = {
    {"phím", (PyCFunction)pysqlite_row_keys, METH_NOARGS,
        PyDoc_STR("Trả về khóa của hàng.")},
    {NULL, NULL}
};
PyDoc_VAR(name)

Khai báo một biến mảng ký tự tĩnh với name đã cho. Mở rộng tới static const char name[]

Ví dụ:

PyDoc_VAR(python_doc) = PyDoc_STR(
   "Một loài rắn thắt lưng thuộc họ Pythonidae bản địa"
   "đến vùng nhiệt đới và cận nhiệt đới của Đông bán cầu.");

Macro tiện ích chung

Các macro sau đây dành cho các tác vụ phổ biến không dành riêng cho Python.

Py_UNUSED(arg)

Sử dụng điều này cho các đối số không được sử dụng trong định nghĩa hàm để tắt các cảnh báo của trình biên dịch. Ví dụ: int func(int a, int Py_UNUSED(b)) { return a; }.

Added in version 3.4.

Py_GCC_ATTRIBUTE(name)

Sử dụng thuộc tính GCC name, ẩn nó khỏi các trình biên dịch không hỗ trợ thuộc tính GCC (chẳng hạn như MSVC).

Điều này mở rộng thành __attribute__((name)) trên trình biên dịch GCC và mở rộng thành không có gì trên các trình biên dịch không hỗ trợ thuộc tính GCC.

Tiện ích số

Py_ABS(x)

Trả về giá trị tuyệt đối của x.

Đối số có thể được đánh giá nhiều lần. Do đó, không chuyển trực tiếp biểu thức có tác dụng phụ tới macro này.

Nếu kết quả không thể được biểu thị (ví dụ: nếu x có giá trị INT_MIN cho loại int), thì hành vi đó không được xác định.

Tương ứng với ((x) < 0 ? -(x) : (x))

Added in version 3.3.

Py_MAX(x, y)
Py_MIN(x, y)

Trả về giá trị lớn hơn hoặc nhỏ hơn của các đối số tương ứng.

Bất kỳ đối số nào cũng có thể được đánh giá nhiều lần. Do đó, không chuyển trực tiếp biểu thức có tác dụng phụ tới macro này.

Py_MAX tương ứng với (((x) > (y)) ? (x) : (y)).

Added in version 3.3.

Py_ARITHMETIC_RIGHT_SHIFT(type, integer, positions)

Tương tự như integer >> positions, nhưng buộc phải mở rộng dấu, vì tiêu chuẩn C không xác định liệu dịch chuyển sang phải của số nguyên có dấu sẽ thực hiện mở rộng dấu hay điền số 0.

integer phải là loại số nguyên có dấu bất kỳ. positions là số vị trí cần dịch chuyển sang phải.

Cả integerpositions đều có thể được đánh giá nhiều lần; do đó, hãy tránh chuyển trực tiếp lệnh gọi hàm hoặc một số thao tác khác có tác dụng phụ tới macro này. Thay vào đó, hãy lưu kết quả dưới dạng một biến rồi chuyển nó.

type không được sử dụng và chỉ được giữ lại để tương thích ngược. Trong lịch sử, type được sử dụng để tạo integer.

Thay đổi trong phiên bản 3.1: Macro này hiện hợp lệ cho tất cả các loại số nguyên có dấu, không chỉ những loại mà unsigned type là hợp lệ. Kết quả là type không còn được sử dụng nữa.

Py_CHARMASK(c)

Đối số phải là một ký tự hoặc số nguyên trong phạm vi [-128, 127] hoặc [0, 255]. Macro này trả về c truyền thành unsigned char.

Tiện ích khẳng định

Py_UNREACHABLE()

Sử dụng tính năng này khi bạn có đường dẫn mã mà thiết kế không thể tiếp cận được. Ví dụ: trong mệnh đề default: trong câu lệnh switch mà tất cả các giá trị có thể có đều nằm trong câu lệnh case. Hãy sử dụng tính năng này ở những nơi mà bạn có thể muốn thực hiện cuộc gọi assert(0) hoặc abort().

Ở chế độ phát hành, macro giúp trình biên dịch tối ưu hóa mã và tránh cảnh báo về mã không thể truy cập được. Ví dụ: macro được triển khai với __builtin_unreachable() trên GCC ở chế độ phát hành.

Trong chế độ gỡ lỗi và trên các trình biên dịch không được hỗ trợ, macro sẽ mở rộng thành lệnh gọi tới Py_FatalError().

Việc sử dụng Py_UNREACHABLE() là thực hiện lệnh gọi đến một hàm không bao giờ trả về nhưng hàm đó không được khai báo _Noreturn.

Nếu đường dẫn mã rất khó xảy ra nhưng có thể truy cập được trong trường hợp đặc biệt thì không được sử dụng macro này. Ví dụ: trong điều kiện bộ nhớ thấp hoặc nếu lệnh gọi hệ thống trả về giá trị ngoài phạm vi dự kiến. Trong trường hợp này, tốt hơn hết bạn nên báo lỗi cho người gọi. Nếu không thể báo lỗi cho người gọi thì có thể sử dụng Py_FatalError().

Added in version 3.7.

Py_SAFE_DOWNCAST(value, larger, smaller)

Truyền value để nhập smaller từ loại larger, xác thực rằng không có thông tin nào bị mất.

Trên các bản phát hành của Python, điều này gần tương đương với ((smaller) value) (trong C++, static_cast<smaller>(value) sẽ được sử dụng thay thế).

Trên các bản dựng gỡ lỗi (ngụ ý rằng Py_DEBUG đã được xác định), điều này khẳng định rằng không có thông tin nào bị mất với dàn diễn viên từ larger đến smaller.

value, largersmaller đều có thể được đánh giá nhiều lần trong biểu thức; do đó, không chuyển trực tiếp biểu thức có tác dụng phụ tới macro này.

Py_BUILD_ASSERT(cond)

Khẳng định điều kiện thời gian biên dịch cond dưới dạng một câu lệnh. Quá trình xây dựng sẽ thất bại nếu điều kiện sai hoặc không thể đánh giá được tại thời điểm biên dịch.

Tương ứng với static_assert(cond) trên C23 trở lên.

Ví dụ:

Py_BUILD_ASSERT(sizeof(PyTime_t) == sizeof(int64_t));

Added in version 3.3.

Py_BUILD_ASSERT_EXPR(cond)

Khẳng định điều kiện thời gian biên dịch cond, dưới dạng biểu thức đánh giá thành 0. Quá trình xây dựng sẽ thất bại nếu điều kiện sai hoặc không thể đánh giá được tại thời điểm biên dịch.

Ví dụ:

#define foo_to_char(foo) \
    ((char *)(foo) + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0))

Added in version 3.3.

Tiện ích kích thước loại

Py_ARRAY_LENGTH(array)

Tính toán độ dài của mảng C được phân bổ tĩnh tại thời điểm biên dịch.

Đối số array phải là mảng C có kích thước được xác định tại thời điểm biên dịch. Việc truyền một mảng có kích thước không xác định, chẳng hạn như mảng được phân bổ vùng heap, sẽ dẫn đến lỗi biên dịch trên một số trình biên dịch hoặc tạo ra kết quả không chính xác.

Điều này gần tương đương với:

sizeof(mảng) / sizeof((mảng)[0])
Py_MEMBER_SIZE(type, member)

Trả về kích thước của cấu trúc (type) member tính bằng byte.

Tương ứng với sizeof(((type *)NULL)->member).

Added in version 3.6.

Tiện ích định nghĩa macro

Py_FORCE_EXPANSION(X)

Điều này tương đương với X, rất hữu ích cho việc dán mã thông báo trong macro, vì việc mở rộng macro trong X được bộ tiền xử lý đánh giá một cách bắt buộc.

Py_STRINGIFY(x)

Chuyển đổi x thành chuỗi C. Ví dụ: Py_STRINGIFY(123) trả về "123".

Added in version 3.4.

Tiện ích khai báo

Các macro sau có thể được sử dụng trong khai báo. Chúng hữu ích nhất để xác định chính C API và được sử dụng hạn chế đối với các tác giả mở rộng. Hầu hết chúng mở rộng sang cách viết dành riêng cho trình biên dịch của các phần mở rộng phổ biến cho ngôn ngữ C.

Py_ALWAYS_INLINE

Yêu cầu trình biên dịch luôn nội tuyến một hàm nội tuyến tĩnh. Trình biên dịch có thể bỏ qua nó và quyết định không nội tuyến hàm.

Tương ứng với thuộc tính always_inline trong GCC và __forceinline trong MSVC.

Nó có thể được sử dụng để thực hiện nội tuyến các hàm nội tuyến tĩnh quan trọng khi xây dựng Python ở chế độ gỡ lỗi với chức năng nội tuyến bị vô hiệu hóa. Ví dụ: MSC vô hiệu hóa chức năng nội tuyến khi xây dựng ở chế độ gỡ lỗi.

Đánh dấu một cách mù quáng một hàm nội tuyến tĩnh bằng Py_ALWAYS_INLINE có thể dẫn đến hiệu suất kém hơn (ví dụ do kích thước mã tăng lên). Trình biên dịch thường thông minh hơn nhà phát triển trong việc phân tích chi phí/lợi ích.

Nếu Python là built in debug mode (nếu macro Py_DEBUG được xác định), macro Py_ALWAYS_INLINE sẽ không làm gì cả.

Nó phải được chỉ định trước kiểu trả về của hàm. Cách sử dụng:

nội tuyến tĩnh Py_ALWAYS_INLINE int ngẫu nhiên(void) { return 4; }

Added in version 3.11.

Py_NO_INLINE

Vô hiệu hóa nội tuyến trên một chức năng. Ví dụ: nó làm giảm mức tiêu thụ ngăn xếp C: hữu ích trên các bản dựng LTO+PGO có nhiều mã nội tuyến (xem bpo-33720).

Tương ứng với thuộc tính/thông số kỹ thuật noinline trên GCC và MSVC.

Cách sử dụng:

Py_NO_INLINE static int ngẫu nhiên(void) { return 4; }

Added in version 3.11.

Py_DEPRECATED(version)

Sử dụng quyền này để khai báo các API không được dùng nữa trong phiên bản CPython cụ thể. Macro phải được đặt trước tên biểu tượng.

Ví dụ:

Py_DEPRECATED(3.8) PyAPI_FUNC(int) Py_OldFunction(void);

Thay đổi trong phiên bản 3.8: hỗ trợ MSVC đã được thêm vào.

Py_LOCAL(type)

Khai báo một hàm trả về type đã chỉ định bằng cách sử dụng vòng loại gọi nhanh cho các hàm cục bộ trong tệp hiện tại. Về mặt ngữ nghĩa, điều này tương đương với static type.

Py_LOCAL_INLINE(type)

Tương đương với Py_LOCAL nhưng còn yêu cầu nội tuyến hàm này.

Py_LOCAL_SYMBOL

Macro dùng để khai báo một ký hiệu là cục bộ của thư viện dùng chung (ẩn). Trên các nền tảng được hỗ trợ, nó đảm bảo biểu tượng không bị xuất.

Trên các phiên bản tương thích của GCC/Clang, nó sẽ mở rộng thành __attribute__((visibility("hidden"))).

Py_EXPORTED_SYMBOL

Macro dùng để khai báo một ký hiệu (hàm hoặc dữ liệu) khi được xuất. Trên Windows, điều này mở rộng tới __declspec(dllexport). Trên các phiên bản tương thích của GCC/Clang, nó sẽ mở rộng thành __attribute__((visibility("default"))). Macro này dùng để xác định chính C API; mô-đun mở rộng không nên sử dụng nó.

Py_IMPORTED_SYMBOL

Macro dùng để khai báo ký hiệu là đã được nhập. Trên Windows, điều này mở rộng tới __declspec(dllimport). Macro này dùng để xác định chính C API; mô-đun mở rộng không nên sử dụng nó.

PyAPI_FUNC(type)

Macro được CPython sử dụng để khai báo hàm như một phần của C API. Việc mở rộng của nó phụ thuộc vào nền tảng và cấu hình bản dựng. Macro này nhằm mục đích xác định chính C API của CPython; các mô-đun mở rộng không nên sử dụng nó cho các ký hiệu riêng của chúng.

PyAPI_DATA(type)

Macro được CPython sử dụng để khai báo biến toàn cục công khai như một phần của C API. Việc mở rộng của nó phụ thuộc vào nền tảng và cấu hình bản dựng. Macro này nhằm mục đích xác định chính C API của CPython; các mô-đun mở rộng không nên sử dụng nó cho các ký hiệu riêng của chúng.

Macro lỗi thời

Các macro sau đã được sử dụng cho các tính năng đã được chuẩn hóa trong C11.

Py_ALIGNED(num)

Chỉ định căn chỉnh theo byte num trên các trình biên dịch hỗ trợ nó.

Hãy cân nhắc sử dụng công cụ xác định _Alignas tiêu chuẩn C11 trên macro này.

Py_LL(number)
Py_ULL(number)

Sử dụng number làm số nguyên long long hoặc unsigned long long tương ứng.

Mở rộng thành number, theo sau là LL hoặc LLU, nhưng sẽ mở rộng sang một số hậu tố dành riêng cho trình biên dịch trên một số trình biên dịch cũ hơn.

Hãy cân nhắc việc sử dụng trực tiếp các hậu tố tiêu chuẩn C99 là LLLLU.

Py_MEMCPY(dest, src, n)

Đây là bí danh soft deprecated của memcpy(). Thay vào đó hãy sử dụng memcpy() trực tiếp.

Sắp loại bỏ từ phiên bản 3.14: Macro là soft deprecated.

Py_VA_COPY

Đây là bí danh soft deprecated cho hàm va_copy tiêu chuẩn C99.

Về mặt lịch sử, điều này sẽ sử dụng một phương pháp dành riêng cho trình biên dịch để sao chép va_list.

Thay đổi trong phiên bản 3.6: Đây hiện là bí danh của va_copy.

Đối tượng, loại và số lượng tham chiếu

Hầu hết các hàm API của Python/C đều có một hoặc nhiều đối số cũng như giá trị trả về thuộc loại PyObject*. Kiểu này là một con trỏ tới một kiểu dữ liệu mờ biểu thị một đối tượng Python tùy ý. Vì tất cả các loại đối tượng Python đều được ngôn ngữ Python xử lý theo cùng một cách trong hầu hết các tình huống (ví dụ: bài tập, quy tắc phạm vi và truyền đối số), nên điều phù hợp là chúng phải được biểu thị bằng một loại C duy nhất. Hầu hết tất cả các đối tượng Python đều tồn tại trên heap: bạn không bao giờ khai báo biến tự động hoặc biến tĩnh thuộc loại PyObject, chỉ có thể khai báo các biến con trỏ thuộc loại PyObject*. Ngoại lệ duy nhất là các đối tượng kiểu; vì chúng không bao giờ được hủy phân bổ nên chúng thường là các đối tượng PyTypeObject tĩnh.

Tất cả các đối tượng Python (kể cả số nguyên Python) đều có typereference count. Kiểu của một đối tượng xác định loại đối tượng đó là gì (ví dụ: số nguyên, danh sách hoặc hàm do người dùng xác định; còn nhiều đối tượng khác như được giải thích trong Hệ thống phân cấp loại tiêu chuẩn). Đối với mỗi loại phổ biến, có một macro để kiểm tra xem một đối tượng có thuộc loại đó hay không; ví dụ: PyList_Check(a) đúng nếu (và chỉ khi) đối tượng được trỏ tới bởi a là một danh sách Python.

Số lượng tham chiếu

Số lượng tham chiếu rất quan trọng vì máy tính ngày nay có kích thước bộ nhớ hữu hạn (và thường bị giới hạn nghiêm trọng); nó đếm xem có bao nhiêu địa điểm khác nhau có strong reference cho một đối tượng. Một địa điểm như vậy có thể là một đối tượng khác hoặc một biến C toàn cục (hoặc tĩnh) hoặc một biến cục bộ trong một số hàm C. Khi strong reference cuối cùng tới một đối tượng được giải phóng (tức là số tham chiếu của nó trở thành 0), đối tượng đó sẽ bị hủy phân bổ. Nếu nó chứa các tham chiếu đến các đối tượng khác, các tham chiếu đó sẽ được giải phóng. Những đối tượng khác đó có thể lần lượt bị hủy phân bổ nếu không còn tham chiếu nào đến chúng nữa, v.v. (Có một vấn đề rõ ràng với các đối tượng tham chiếu lẫn nhau ở đây; hiện tại, giải pháp là "đừng làm điều đó.")

Số lượng tham chiếu luôn được thao tác rõ ràng. Cách thông thường là sử dụng macro Py_INCREF() để lấy một tham chiếu mới đến một đối tượng (tức là tăng số lượng tham chiếu của nó lên một) và Py_DECREF() để giải phóng tham chiếu đó (tức là giảm số lượng tham chiếu đi một). Macro Py_DECREF() phức tạp hơn đáng kể so với macro incref, vì nó phải kiểm tra xem số tham chiếu có bằng 0 hay không và sau đó khiến bộ giải phóng của đối tượng được gọi. Bộ giải phóng là một con trỏ hàm có trong cấu trúc kiểu của đối tượng. Trình giải phóng dành riêng cho loại sẽ đảm nhiệm việc giải phóng các tham chiếu cho các đối tượng khác có trong đối tượng nếu đây là loại đối tượng phức hợp, chẳng hạn như danh sách, cũng như thực hiện bất kỳ quyết toán bổ sung nào cần thiết. Không có khả năng số lượng tham chiếu có thể tràn; ít nhất có bao nhiêu bit được sử dụng để giữ số tham chiếu vì có các vị trí bộ nhớ riêng biệt trong bộ nhớ ảo (giả sử sizeof(Py_ssize_t) >= sizeof(void*)). Vì vậy, việc tăng số lượng tham chiếu là một thao tác đơn giản.

Không cần thiết phải giữ strong reference (tức là tăng số lượng tham chiếu) cho mọi biến cục bộ có chứa con trỏ tới một đối tượng. Về lý thuyết, số tham chiếu của đối tượng tăng lên một khi biến được tạo để trỏ tới nó và giảm đi một khi biến nằm ngoài phạm vi. Tuy nhiên, hai cái này triệt tiêu lẫn nhau nên cuối cùng số lượng tham chiếu không thay đổi. Lý do thực sự duy nhất để sử dụng số tham chiếu là để ngăn đối tượng bị hủy phân bổ miễn là biến của chúng ta đang trỏ đến nó. Nếu chúng ta biết rằng có ít nhất một tham chiếu khác đến đối tượng tồn tại ít nhất bằng biến của chúng ta thì tạm thời không cần phải lấy một strong reference mới (tức là tăng số lượng tham chiếu). Một tình huống quan trọng mà điều này phát sinh là trong các đối tượng được truyền dưới dạng đối số cho các hàm C trong mô-đun mở rộng được gọi từ Python; cơ chế cuộc gọi đảm bảo giữ tham chiếu đến mọi đối số trong suốt thời gian của cuộc gọi.

Tuy nhiên, một cạm bẫy phổ biến là trích xuất một đối tượng từ danh sách và giữ nó trong một thời gian mà không lấy tham chiếu mới. Một số thao tác khác có thể loại bỏ đối tượng khỏi danh sách, giải phóng tham chiếu đó và có thể giải phóng nó. Mối nguy hiểm thực sự là các hoạt động trông có vẻ vô hại có thể gọi mã Python tùy ý có thể thực hiện điều này; có một đường dẫn mã cho phép quyền kiểm soát được chuyển trở lại người dùng từ Py_DECREF(), vì vậy hầu hết mọi thao tác đều tiềm ẩn nguy hiểm.

Một cách tiếp cận an toàn là luôn sử dụng các thao tác chung (các hàm có tên bắt đầu bằng PyObject_, PyNumber_, PySequence_ hoặc PyMapping_). Các thao tác này luôn tạo một strong reference mới (tức là tăng số lượng tham chiếu) của đối tượng mà chúng trả về. Điều này khiến người gọi có trách nhiệm gọi Py_DECREF() khi họ hoàn thành kết quả; điều này sẽ sớm trở thành bản chất thứ hai.

Chi tiết số lượng tham chiếu

Hành vi đếm tham chiếu của các hàm trong Python/C API được giải thích rõ nhất dưới dạng ownership of references. Quyền sở hữu liên quan đến các tài liệu tham khảo, không bao giờ liên quan đến các đối tượng (các đối tượng không được sở hữu: chúng luôn được chia sẻ). "Sở hữu một tài liệu tham khảo" có nghĩa là chịu trách nhiệm gọi Py_DECREF trên đó khi tài liệu tham khảo không còn cần thiết nữa. Quyền sở hữu cũng có thể được chuyển giao, nghĩa là mã nhận được quyền sở hữu tham chiếu sau đó sẽ chịu trách nhiệm giải phóng nó bằng cách gọi Py_DECREF() hoặc Py_XDECREF() khi không còn cần thiết---hoặc chuyển giao trách nhiệm này (thường là cho người gọi nó). Khi một hàm chuyển quyền sở hữu tham chiếu cho người gọi nó, người gọi đó được cho là đã nhận được tham chiếu new. Khi không có quyền sở hữu nào được chuyển giao, người gọi sẽ được gọi tới borrow tham chiếu. Không cần phải làm gì đối với borrowed reference.

Ngược lại, khi hàm gọi chuyển tham chiếu đến một đối tượng, có hai khả năng: hàm steals tham chiếu đến đối tượng hoặc không. Stealing a reference có nghĩa là khi bạn chuyển một tham chiếu đến một hàm, hàm đó sẽ giả định rằng nó hiện sở hữu tham chiếu đó và bạn không chịu trách nhiệm về nó nữa.

Rất ít chức năng lấy cắp tài liệu tham khảo; hai trường hợp ngoại lệ đáng chú ý là PyList_SetItem()PyTuple_SetItem(), lấy cắp tham chiếu đến mục (nhưng không lấy cắp tham chiếu đến bộ dữ liệu hoặc danh sách mà mục đó được đặt vào đó!). Các hàm này được thiết kế để đánh cắp một tham chiếu vì một thành ngữ phổ biến để điền một bộ dữ liệu hoặc danh sách với các đối tượng mới được tạo; ví dụ: mã để tạo bộ dữ liệu (1, 2, "three") có thể trông như thế này (tạm thời quên việc xử lý lỗi; cách tốt hơn để viết mã này được hiển thị bên dưới):

PyObject *t;

t = PyTuple_New(3);
PyTuple_SetItem(t, 0, PyLong_FromLong(1L));
PyTuple_SetItem(t, 1, PyLong_FromLong(2L));
PyTuple_SetItem(t, 2, PyUnicode_FromString("ba"));

Ở đây, PyLong_FromLong() trả về một tham chiếu mới ngay lập tức bị PyTuple_SetItem() đánh cắp. Khi bạn muốn tiếp tục sử dụng một đối tượng mặc dù tham chiếu đến nó sẽ bị đánh cắp, hãy sử dụng Py_INCREF() để lấy một tham chiếu khác trước khi gọi hàm đánh cắp tham chiếu.

Ngẫu nhiên, PyTuple_SetItem() là cách only để đặt các mục tuple; PySequence_SetItem()PyObject_SetItem() từ chối thực hiện việc này vì bộ dữ liệu là loại dữ liệu bất biến. Bạn chỉ nên sử dụng PyTuple_SetItem() cho các bộ dữ liệu mà bạn tự tạo.

Mã tương đương để điền danh sách có thể được viết bằng PyList_New()PyList_SetItem().

Tuy nhiên, trong thực tế, bạn sẽ hiếm khi sử dụng những cách tạo và điền một bộ hoặc danh sách này. Có một hàm chung, Py_BuildValue(), có thể tạo hầu hết các đối tượng phổ biến từ các giá trị C, được điều khiển bởi format string. Ví dụ: hai khối mã trên có thể được thay thế bằng khối mã sau (cũng đảm nhiệm việc kiểm tra lỗi):

Danh sách PyObject *tuple, *;

tuple = Py_BuildValue("(iis)", 1, 2, "ba");
list = Py_BuildValue("[iis]", 1, 2, "ba");

Việc sử dụng PyObject_SetItem() và bạn bè với các mục có tài liệu tham khảo mà bạn chỉ mượn là phổ biến hơn nhiều, chẳng hạn như các đối số được chuyển vào hàm bạn đang viết. Trong trường hợp đó, hành vi của họ đối với tài liệu tham khảo sẽ sáng suốt hơn nhiều, vì bạn không cần phải lấy một tài liệu tham khảo mới chỉ để có thể đưa tài liệu tham khảo đó đi ("nếu nó bị đánh cắp"). Ví dụ: hàm này đặt tất cả các mục của danh sách (thực tế là bất kỳ chuỗi có thể thay đổi nào) thành một mục nhất định

int
set_all(PyObject *target, PyObject *item)
{
    Py_ssize_t i, n;

    n = PyObject_Length(đích);
    nếu (n < 0)
        trả về -1;
     (i = 0; i < n; i++) {
        PyObject *index = PyLong_FromSsize_t(i);
        nếu (!chỉ mục)
            trả về -1;
        if (PyObject_SetItem(đích, chỉ mục, mục) < 0) {
            Py_DECREF(chỉ mục);
            trả về -1;
        }
        Py_DECREF(chỉ mục);
    }
    trả về 0;
}

Tình huống hơi khác đối với các giá trị trả về của hàm. Mặc dù việc chuyển tham chiếu đến hầu hết các hàm không làm thay đổi trách nhiệm sở hữu của bạn đối với tham chiếu đó nhưng nhiều hàm trả về tham chiếu đến một đối tượng sẽ cung cấp cho bạn quyền sở hữu tham chiếu đó. Lý do rất đơn giản: trong nhiều trường hợp, đối tượng trả về được tạo nhanh chóng và tham chiếu bạn nhận được là tham chiếu duy nhất đến đối tượng. Do đó, các hàm chung trả về tham chiếu đối tượng, như PyObject_GetItem()PySequence_GetItem(), luôn trả về tham chiếu mới (người gọi trở thành chủ sở hữu của tham chiếu).

Điều quan trọng là phải nhận ra rằng việc bạn có sở hữu một tham chiếu được hàm trả về hay không phụ thuộc vào hàm bạn gọi chỉ --- the plumage (loại đối tượng được truyền dưới dạng đối số cho hàm) doesn't enter into it! Do đó, nếu bạn trích xuất một mục từ danh sách bằng PyList_GetItem(), thì bạn không sở hữu tham chiếu đó --- nhưng nếu bạn lấy cùng một mục từ cùng một danh sách bằng cách sử dụng PySequence_GetItem() (điều này nhận chính xác các đối số giống nhau), thì bạn sở hữu một tham chiếu đến đối tượng được trả về.

Đây là một ví dụ về cách bạn có thể viết một hàm tính tổng các phần tử trong một danh sách các số nguyên; một lần sử dụng PyList_GetItem() và một lần sử dụng PySequence_GetItem().

dài
sum_list(danh sách PyObject *)
{
    Py_ssize_t i, n;
    tổng dài = 0, giá trị;
    mục PyObject *;

    n = PyList_Size(danh sách);
    nếu (n < 0)
        trả về -1; /* Không phải danh sách */
     (i = 0; i < n; i++) {
        item = PyList_GetItem(list, i); /*Không thể thất bại*/
        if (!PyLong_Check(item)) tiếp tục; /* Bỏ qua các số không nguyên */
        giá trị = PyLong_AsLong(item);
        if (giá trị == -1 && PyErr_Occurred())
            /* Số nguyên quá lớn không thể nhét vừa một chữ C dài, giải cứu */
            trả về -1;
        tổng giá trị +=;
    }
    tổng số tiền trả lại;
}
dài
sum_sequence(PyObject *sequence)
{
    Py_ssize_t i, n;
    tổng dài = 0, giá trị;
    mục PyObject *;
    n = PySequence_Length(chuỗi);
    nếu (n < 0)
        trả về -1; /* Không có độ dài */
     (i = 0; i < n; i++) {
        item = PySequence_GetItem(sequence, i);
        nếu (mục == NULL)
            trả về -1; /* Không phải trình tự hoặc lỗi khác */
        if (PyLong_Check(item)) {
            giá trị = PyLong_AsLong(item);
            Py_DECREF(vật phẩm);
            if (giá trị == -1 && PyErr_Occurred())
                /* Số nguyên quá lớn không thể nhét vừa một chữ C dài, giải cứu */
                trả về -1;
            tổng giá trị +=;
        }
        khác {
            Py_DECREF(vật phẩm); /* Loại bỏ quyền sở hữu tài liệu tham khảo */
        }
    }
    tổng số tiền trả lại;
}

Các loại

Có một số loại dữ liệu khác đóng vai trò quan trọng trong Python/C API; hầu hết là các loại C đơn giản như int, long, doublechar*. Một số loại cấu trúc được sử dụng để mô tả các bảng tĩnh dùng để liệt kê các hàm được mô-đun xuất hoặc thuộc tính dữ liệu của một loại đối tượng mới và một loại khác được sử dụng để mô tả giá trị của số phức. Những điều này sẽ được thảo luận cùng với các chức năng sử dụng chúng.

type Py_ssize_t
Một phần của ABI ổn định.

Loại tích phân có dấu sao cho sizeof(Py_ssize_t) == sizeof(size_t). C99 không định nghĩa trực tiếp điều đó (size_t là loại tích phân không dấu). Xem PEP 353 để biết chi tiết. PY_SSIZE_T_MAX là giá trị dương lớn nhất của loại Py_ssize_t.

Ngoại lệ

Lập trình viên Python chỉ cần xử lý các trường hợp ngoại lệ nếu cần xử lý lỗi cụ thể; các trường hợp ngoại lệ chưa được xử lý sẽ tự động được truyền tới người gọi, sau đó đến người gọi của người gọi, v.v., cho đến khi chúng đến được trình thông dịch cấp cao nhất, nơi chúng được báo cáo cho người dùng kèm theo dấu vết ngăn xếp.

Tuy nhiên, đối với các lập trình viên C, việc kiểm tra lỗi luôn phải rõ ràng. Tất cả các hàm trong Python/C API đều có thể đưa ra các ngoại lệ, trừ khi có yêu cầu rõ ràng được đưa ra khác đi trong tài liệu của hàm. Nói chung, khi một hàm gặp lỗi, nó sẽ đặt một ngoại lệ, loại bỏ mọi tham chiếu đối tượng mà nó sở hữu và trả về một chỉ báo lỗi. Nếu không được ghi lại, chỉ báo này là NULL hoặc -1, tùy thuộc vào kiểu trả về của hàm. Một số hàm trả về kết quả Boolean đúng/sai, với kết quả sai biểu thị lỗi. Rất ít hàm không trả về chỉ báo lỗi rõ ràng hoặc có giá trị trả về không rõ ràng và yêu cầu kiểm tra rõ ràng các lỗi với PyErr_Occurred(). Những ngoại lệ này luôn được ghi lại rõ ràng.

Trạng thái ngoại lệ được duy trì trong bộ lưu trữ trên mỗi luồng (điều này tương đương với việc sử dụng bộ nhớ chung trong một ứng dụng không theo chuỗi). Một thread có thể ở một trong hai trạng thái: có ngoại lệ xảy ra hoặc không. Hàm PyErr_Occurred() có thể được sử dụng để kiểm tra điều này: nó trả về một tham chiếu mượn đến đối tượng loại ngoại lệ khi một ngoại lệ xảy ra, và NULL nếu không. Có một số hàm để đặt trạng thái ngoại lệ: PyErr_SetString() là hàm phổ biến nhất (mặc dù không phải là chung nhất) để đặt trạng thái ngoại lệ và PyErr_Clear() xóa trạng thái ngoại lệ.

Trạng thái ngoại lệ đầy đủ bao gồm ba đối tượng (tất cả đều có thể là NULL): loại ngoại lệ, giá trị ngoại lệ tương ứng và truy nguyên. Chúng có cùng ý nghĩa với kết quả Python của sys.exc_info(); tuy nhiên, chúng không giống nhau: các đối tượng Python biểu thị ngoại lệ cuối cùng được xử lý bởi câu lệnh Python try ... except, trong khi trạng thái ngoại lệ cấp C chỉ tồn tại trong khi một ngoại lệ đang được truyền giữa các hàm C cho đến khi nó đến vòng lặp chính của trình thông dịch mã byte Python, vòng lặp này sẽ đảm nhiệm việc chuyển nó sang sys.exc_info() và những người bạn.

Lưu ý rằng bắt đầu với Python 1.5, cách ưu tiên, an toàn theo luồng để truy cập trạng thái ngoại lệ từ mã Python là gọi hàm sys.exc_info(), hàm này trả về trạng thái ngoại lệ trên mỗi luồng cho mã Python. Ngoài ra, ngữ nghĩa của cả hai cách truy cập trạng thái ngoại lệ đã thay đổi để hàm bắt ngoại lệ sẽ lưu và khôi phục trạng thái ngoại lệ của luồng của nó để duy trì trạng thái ngoại lệ của người gọi nó. Điều này ngăn ngừa các lỗi phổ biến trong mã xử lý ngoại lệ do một hàm trông có vẻ vô hại ghi đè lên ngoại lệ đang được xử lý; nó cũng làm giảm thời gian kéo dài thời gian không mong muốn thường xuyên đối với các đối tượng được tham chiếu bởi các khung ngăn xếp trong truy nguyên.

Theo nguyên tắc chung, một hàm gọi hàm khác để thực hiện một số tác vụ phải kiểm tra xem hàm được gọi có đưa ra ngoại lệ hay không và nếu có thì chuyển trạng thái ngoại lệ đó cho hàm gọi nó. Nó sẽ loại bỏ mọi tham chiếu đối tượng mà nó sở hữu và trả về một chỉ báo lỗi, nhưng not sẽ đặt một ngoại lệ khác --- điều đó sẽ ghi đè lên ngoại lệ vừa được nêu ra và làm mất thông tin quan trọng về nguyên nhân chính xác của lỗi.

Một ví dụ đơn giản về việc phát hiện các ngoại lệ và chuyển chúng đi được hiển thị trong ví dụ sum_sequence() ở trên. Điều đó xảy ra là ví dụ này không cần phải xóa bất kỳ tham chiếu nào được sở hữu khi phát hiện ra lỗi. Hàm ví dụ sau đây hiển thị một số thao tác dọn dẹp lỗi. Đầu tiên, để nhắc bạn tại sao bạn thích Python, chúng tôi hiển thị mã Python tương đương:

def incr_item(dict, key):
    thử:
        mục = dict[key]
    ngoại trừ KeyError:
        mục = 0
    dict[key] = mục + 1

Đây là mã C tương ứng, với tất cả sự vinh quang của nó:

int
incr_item(PyObject *dict, PyObject *key)
{
    /* Tất cả các đối tượng được khởi tạo thành NULL cho Py_XDECREF */
    PyObject *item = NULL, *const_one = NULL, *incremented_item = NULL;
    int rv = -1; /* Trả về giá trị khởi tạo là -1 (lỗi) */

    item = PyObject_GetItem(dict, key);
    nếu (mục == NULL) {
        /* Chỉ xử lý KeyError: */
        if (!PyErr_ExceptionMatches(PyExc_KeyError))
            lỗi đi đến;

        /* Xóa lỗi và sử dụng số 0: */
        PyErr_Clear();
        mục = PyLong_FromLong(0L);
        nếu (mục == NULL)
            lỗi đi đến;
    }
    const_one = PyLong_FromLong(1L);
    nếu (const_one == NULL)
        lỗi đi đến;

    tăng_item = PyNumber_Add(item, const_one);
    if (incremented_item == NULL)
        lỗi đi đến;

    if (PyObject_SetItem(dict, key, tăng_item) < 0)
        lỗi đi đến;
    rv = 0; /*Thành công*/
    /* Tiếp tục với mã dọn dẹp */

 lỗi:
    /* Mã dọn dẹp, được chia sẻ theo đường dẫn thành công và thất bại */

    /* Sử dụng Py_XDECREF() để bỏ qua các tham chiếu NULL */
    Py_XDECREF(vật phẩm);
    Py_XDECREF(const_one);
    Py_XDECREF(tăng_item);

    trở lại rv; /* -1 là lỗi, 0 là thành công */
}

Ví dụ này thể hiện việc sử dụng câu lệnh goto được xác nhận trong C! Nó minh họa việc sử dụng PyErr_ExceptionMatches()PyErr_Clear() để xử lý các trường hợp ngoại lệ cụ thể và việc sử dụng Py_XDECREF() để loại bỏ các tham chiếu sở hữu có thể là NULL (lưu ý 'X' trong tên; Py_DECREF() sẽ gặp sự cố khi gặp một tham chiếu NULL). Điều quan trọng là các biến được sử dụng để chứa các tham chiếu thuộc sở hữu phải được khởi tạo thành NULL để nó hoạt động; tương tự như vậy, giá trị trả về được đề xuất được khởi tạo thành -1 (lỗi) và chỉ được đặt thành thành công sau khi lệnh gọi cuối cùng được thực hiện thành công.

Nhúng Python

Một nhiệm vụ quan trọng mà chỉ những người nhúng (trái ngược với những người viết phần mở rộng) của trình thông dịch Python mới phải lo lắng là việc khởi tạo và có thể là sự hoàn thiện của trình thông dịch Python. Hầu hết các chức năng của trình thông dịch chỉ có thể được sử dụng sau khi trình thông dịch đã được khởi tạo.

Hàm khởi tạo cơ bản là Py_Initialize(). Thao tác này sẽ khởi tạo bảng các mô-đun đã tải và tạo các mô-đun cơ bản builtins, __main__sys. Nó cũng khởi tạo đường dẫn tìm kiếm mô-đun (sys.path).

Py_Initialize() không đặt "danh sách đối số tập lệnh" (sys.argv). Nếu mã Python cần biến này để thực thi sau này, thì phải đặt cài đặt PyConfig.argvPyConfig.parse_argv: xem Python Initialization Configuration.

Trên hầu hết các hệ thống (đặc biệt là trên Unix và Windows, mặc dù các chi tiết hơi khác nhau), Py_Initialize() tính toán đường dẫn tìm kiếm mô-đun dựa trên dự đoán tốt nhất về vị trí của trình thông dịch Python tiêu chuẩn có thể thực thi được, giả sử rằng thư viện Python được tìm thấy ở một vị trí cố định so với tệp thực thi của trình thông dịch Python. Cụ thể, nó tìm kiếm thư mục có tên lib/pythonX.Y liên quan đến thư mục mẹ nơi tìm thấy tệp thực thi có tên python trên đường dẫn tìm kiếm lệnh shell (biến môi trường PATH).

Ví dụ: nếu tìm thấy tệp thực thi Python trong /usr/local/bin/python, nó sẽ cho rằng các thư viện nằm trong /usr/local/lib/pythonX.Y. (Trên thực tế, đường dẫn cụ thể này cũng là vị trí "dự phòng", được sử dụng khi không tìm thấy tệp thực thi có tên python dọc theo PATH.) Người dùng có thể ghi đè hành vi này bằng cách đặt biến môi trường PYTHONHOME hoặc chèn các thư mục bổ sung trước đường dẫn tiêu chuẩn bằng cách đặt PYTHONPATH.

Ứng dụng nhúng có thể điều khiển việc tìm kiếm bằng cách cài đặt PyConfig.program_name before gọi Py_InitializeFromConfig(). Lưu ý rằng PYTHONHOME vẫn ghi đè lên điều này và PYTHONPATH vẫn được chèn ở phía trước đường dẫn chuẩn. Một ứng dụng yêu cầu toàn quyền kiểm soát phải cung cấp cách triển khai Py_GetPath(), Py_GetPrefix(), Py_GetExecPrefix()Py_GetProgramFullPath() của riêng nó (tất cả đều được xác định trong Modules/getpath.c).

Đôi khi, bạn nên "không khởi tạo" Python. Ví dụ: ứng dụng có thể muốn bắt đầu lại (thực hiện một lệnh gọi khác tới Py_Initialize()) hoặc ứng dụng chỉ được thực hiện bằng cách sử dụng Python và muốn giải phóng bộ nhớ do Python phân bổ. Điều này có thể được thực hiện bằng cách gọi Py_FinalizeEx(). Hàm Py_IsInitialized() trả về true nếu Python hiện ở trạng thái khởi tạo. Thông tin thêm về các chức năng này sẽ được đưa ra trong chương sau. Lưu ý rằng Py_FinalizeEx() thực hiện not giải phóng tất cả bộ nhớ do trình thông dịch Python cấp phát, ví dụ: bộ nhớ được phân bổ bởi các mô-đun mở rộng hiện không thể giải phóng được.

Gỡ lỗi bản dựng

Python có thể được xây dựng bằng một số macro để cho phép kiểm tra thêm trình thông dịch và mô-đun mở rộng. Những hoạt động kiểm tra này có xu hướng bổ sung một lượng lớn chi phí vào thời gian chạy nên chúng không được bật theo mặc định.

Danh sách đầy đủ các loại bản dựng gỡ lỗi khác nhau có trong tệp Misc/SpecialBuilds.txt trong bản phân phối nguồn Python. Các bản dựng sẵn có hỗ trợ theo dõi số lượng tham chiếu, gỡ lỗi bộ cấp phát bộ nhớ hoặc lập hồ sơ cấp thấp của vòng lặp trình thông dịch chính. Chỉ những bản dựng được sử dụng thường xuyên nhất mới được mô tả trong phần còn lại của phần này.

Py_DEBUG

Biên dịch trình thông dịch với macro Py_DEBUG được xác định sẽ tạo ra ý nghĩa chung của a debug build of Python. Py_DEBUG được kích hoạt trong bản dựng Unix bằng cách thêm --with-pydebug vào lệnh ./configure. Nó cũng được ngụ ý bởi sự hiện diện của macro _DEBUG không dành riêng cho Python. Khi Py_DEBUG được bật trong bản dựng Unix, tối ưu hóa trình biên dịch sẽ bị tắt.

Ngoài việc gỡ lỗi số tham chiếu được mô tả bên dưới, các kiểm tra bổ sung cũng được thực hiện, xem Python Debug Build.

Việc xác định Py_TRACE_REFS cho phép theo dõi tham chiếu (xem configure --with-trace-refs option). Khi được xác định, một danh sách các đối tượng hoạt động được liên kết đôi theo vòng tròn sẽ được duy trì bằng cách thêm hai trường bổ sung vào mỗi PyObject. Tổng phân bổ cũng được theo dõi. Khi thoát, tất cả các tài liệu tham khảo hiện có sẽ được in. (Trong chế độ tương tác, điều này xảy ra sau mỗi câu lệnh do trình thông dịch chạy.)

Vui lòng tham khảo Misc/SpecialBuilds.txt trong bản phân phối nguồn Python để biết thêm thông tin chi tiết.