Giao thức cuộc gọi¶
CPython hỗ trợ hai giao thức gọi khác nhau: tp_call và vectorcall.
Giao thức tp_call¶
Các phiên bản của lớp đặt tp_call có thể gọi được. Chữ ký của slot là:
PyObject *tp_call(PyObject *callable, PyObject *args, PyObject *kwargs);
Lệnh gọi được thực hiện bằng cách sử dụng một bộ dữ liệu cho các đối số vị trí và một lệnh cho các đối số từ khóa, tương tự như callable(*args, **kwargs) trong mã Python. args phải không phải là NULL (sử dụng bộ trống nếu không có đối số) nhưng kwargs có thể là NULL nếu không có đối số từ khóa.
Quy ước này không chỉ được tp_call sử dụng: tp_new và tp_init cũng truyền đối số theo cách này.
Để gọi một đối tượng, hãy sử dụng PyObject_Call() hoặc call API khác.
Giao thức Vectorcall¶
Added in version 3.9.
Giao thức vectorcall được giới thiệu trong PEP 590 như một giao thức bổ sung để thực hiện cuộc gọi hiệu quả hơn.
Theo nguyên tắc chung, CPython sẽ ưu tiên vectorcall cho các cuộc gọi nội bộ nếu cuộc gọi có thể hỗ trợ nó. Tuy nhiên, đây không phải là một quy tắc cứng nhắc. Ngoài ra, một số tiện ích mở rộng của bên thứ ba sử dụng trực tiếp tp_call (thay vì sử dụng PyObject_Call()). Do đó, lớp hỗ trợ vectorcall cũng phải triển khai tp_call. Hơn nữa, khả năng gọi được phải hoạt động giống nhau bất kể giao thức nào được sử dụng. Cách được khuyến nghị để đạt được điều này là đặt tp_call thành PyVectorcall_Call(). Điều này lặp đi lặp lại:
Cảnh báo
Một lớp hỗ trợ vectorcall must cũng triển khai tp_call với cùng ngữ nghĩa.
Thay đổi trong phiên bản 3.12: Cờ Py_TPFLAGS_HAVE_VECTORCALL hiện đã bị xóa khỏi một lớp khi phương thức __call__() của lớp đó được gán lại. (Điều này chỉ đặt tp_call nội bộ và do đó có thể khiến nó hoạt động khác với hàm vectorcall.) Trong các phiên bản Python trước đó, vectorcall chỉ nên được sử dụng với immutable hoặc các kiểu tĩnh.
Một lớp không nên triển khai vectorcall nếu tốc độ đó chậm hơn tp_call. Ví dụ: nếu callee cần chuyển đổi các đối số thành một bộ args và kwargs dict thì việc triển khai vectorcall sẽ chẳng ích gì.
Các lớp có thể triển khai giao thức vectorcall bằng cách bật cờ Py_TPFLAGS_HAVE_VECTORCALL và đặt tp_vectorcall_offset thành offset bên trong cấu trúc đối tượng nơi vectorcallfunc xuất hiện. Đây là một con trỏ tới một hàm có chữ ký sau:
-
typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)¶
- Một phần của ABI ổn định kể từ phiên bản 3.12.
callable là đối tượng được gọi.
- args là một mảng C bao gồm các đối số vị trí theo sau là
giá trị của các đối số từ khóa. Đây có thể là NULL nếu không có đối số.
- nargsf là số lượng đối số vị trí cộng với có thể là
cờ
PY_VECTORCALL_ARGUMENTS_OFFSET. Để lấy số lượng đối số vị trí thực tế từ nargsf, hãy sử dụngPyVectorcall_NARGS().
- kwnames là một bộ chứa tên của các đối số từ khóa;
nói cách khác, chìa khóa của lệnh kwargs. Những tên này phải là chuỗi (các phiên bản của
strhoặc một lớp con) và chúng phải là duy nhất. Nếu không có đối số từ khóa thì thay vào đó, kwnames có thể là NULL.
-
PY_VECTORCALL_ARGUMENTS_OFFSET¶
- Một phần của ABI ổn định kể từ phiên bản 3.12.
Nếu cờ này được đặt trong đối số vectorcall nargsf, callee được phép tạm thời thay đổi
args[-1]. Nói cách khác, args trỏ đến đối số 1 (không phải 0) trong vectơ được phân bổ. Callee phải khôi phục giá trị củaargs[-1]trước khi quay lại.Đối với
PyObject_VectorcallMethod(), cờ này có nghĩa làargs[0]có thể bị thay đổi.Bất cứ khi nào họ có thể thực hiện điều đó với giá rẻ (không cần phân bổ thêm), người gọi được khuyến khích sử dụng
PY_VECTORCALL_ARGUMENTS_OFFSET. Làm như vậy sẽ cho phép các phương thức có thể gọi như các phương thức bị ràng buộc thực hiện các lệnh gọi tiếp theo (bao gồm đối số self được thêm vào trước) rất hiệu quả.Added in version 3.8.
Để gọi một đối tượng thực hiện vectorcall, hãy sử dụng hàm call API như với bất kỳ lệnh gọi nào khác. PyObject_Vectorcall() thường sẽ hiệu quả nhất.
Kiểm soát đệ quy¶
Khi sử dụng tp_call, người được gọi không cần lo lắng về recursion: CPython sử dụng Py_EnterRecursiveCall() và Py_LeaveRecursiveCall() cho các cuộc gọi được thực hiện bằng tp_call.
Để đạt hiệu quả, đây không phải là trường hợp đối với các cuộc gọi được thực hiện bằng vectorcall: người được gọi nên sử dụng Py_EnterRecursiveCall và Py_LeaveRecursiveCall nếu cần.
Hỗ trợ Vectorcall API¶
-
Py_ssize_t PyVectorcall_NARGS(size_t nargsf)¶
- Một phần của ABI ổn định kể từ phiên bản 3.12.
Cho một đối số vectorcall nargsf, trả về số lượng đối số thực tế. Hiện tại tương đương với:
(Py_ssize_t)(nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET)
Tuy nhiên, nên sử dụng chức năng
PyVectorcall_NARGSđể cho phép mở rộng trong tương lai.Added in version 3.8.
-
vectorcallfunc PyVectorcall_Function(PyObject *op)¶
Nếu op không hỗ trợ giao thức vectorcall (vì loại này không hỗ trợ hoặc do phiên bản cụ thể không hỗ trợ), hãy trả về NULL. Ngược lại, trả về con trỏ hàm vectorcall được lưu trong op. Hàm này không bao giờ đưa ra một ngoại lệ.
Điều này chủ yếu hữu ích để kiểm tra xem op có hỗ trợ vectorcall hay không, việc này có thể được thực hiện bằng cách kiểm tra
PyVectorcall_Function(op) != NULL.Added in version 3.9.
-
PyObject *PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict)¶
- Một phần của ABI ổn định kể từ phiên bản 3.12.
Gọi
vectorcallfunccủa callable với các đối số vị trí và từ khóa tương ứng được đưa ra trong một bộ và dict.Đây là một chức năng chuyên biệt, nhằm mục đích đặt vào khe
tp_callhoặc được sử dụng khi triển khaitp_call. Nó không kiểm tra cờPy_TPFLAGS_HAVE_VECTORCALLvà không quay trở lạitp_call.Added in version 3.8.
Gọi đối tượng API¶
Có nhiều hàm khác nhau để gọi một đối tượng Python. Mỗi chuyển đổi các đối số của nó thành một quy ước được hỗ trợ bởi đối tượng được gọi – tp_call hoặc vectorcall. Để thực hiện ít chuyển đổi nhất có thể, hãy chọn một chuyển đổi phù hợp nhất với định dạng dữ liệu bạn có sẵn.
Bảng sau đây tóm tắt các chức năng có sẵn; vui lòng xem tài liệu riêng để biết chi tiết.
Chức năng |
có thể gọi được |
lập luận |
kwargs |
|---|---|---|---|
|
bộ đồ |
dict/ |
|
|
--- |
--- |
|
|
1 đối tượng |
--- |
|
|
tuple/ |
--- |
|
|
định dạng |
--- |
|
obj + |
định dạng |
--- |
|
|
có tính biến đổi |
--- |
|
đối tượng + tên |
có tính biến đổi |
--- |
|
đối tượng + tên |
--- |
--- |
|
đối tượng + tên |
1 đối tượng |
--- |
|
|
vectorgọi |
vectorgọi |
|
|
vectorgọi |
dict/ |
|
đối số + tên |
vectorgọi |
vectorgọi |
-
PyObject *PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)¶
- Giá trị trả về: Tham chiếu mới. Một phần của ABI ổn định.
Gọi một đối tượng Python có thể gọi được là callable, với các đối số được đưa ra bởi bộ args và các đối số được đặt tên do từ điển kwargs đưa ra.
args không được là NULL; sử dụng một bộ trống nếu không cần đối số. Nếu không cần đối số được đặt tên, kwargs có thể là NULL.
Trả về kết quả của cuộc gọi thành công hoặc đưa ra một ngoại lệ và trả về NULL khi thất bại.
Điều này tương đương với biểu thức Python:
callable(*args, **kwargs).
-
PyObject *PyObject_CallNoArgs(PyObject *callable)¶
- Giá trị trả về: Tham chiếu mới. Một phần của ABI ổn định kể từ phiên bản 3.10.
Gọi một đối tượng Python có thể gọi được callable mà không có bất kỳ đối số nào. Đó là cách hiệu quả nhất để gọi một đối tượng Python có thể gọi được mà không cần bất kỳ đối số nào.
Trả về kết quả của cuộc gọi thành công hoặc đưa ra một ngoại lệ và trả về NULL khi thất bại.
Added in version 3.9.
-
PyObject *PyObject_CallOneArg(PyObject *callable, PyObject *arg)¶
- Giá trị trả về: Tham chiếu mới.
Gọi một đối tượng Python có thể gọi được callable với chính xác 1 đối số vị trí arg và không có đối số từ khóa.
Trả về kết quả của cuộc gọi thành công hoặc đưa ra một ngoại lệ và trả về NULL khi thất bại.
Added in version 3.9.
-
PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)¶
- Giá trị trả về: Tham chiếu mới. Một phần của ABI ổn định.
Gọi một đối tượng Python có thể gọi được là callable, với các đối số được cung cấp bởi bộ dữ liệu args. Nếu không cần đối số thì args có thể là NULL.
Trả về kết quả của cuộc gọi thành công hoặc đưa ra một ngoại lệ và trả về NULL khi thất bại.
Điều này tương đương với biểu thức Python:
callable(*args).
-
PyObject *PyObject_CallFunction(PyObject *callable, const char *format, ...)¶
- Giá trị trả về: Tham chiếu mới. Một phần của ABI ổn định.
Gọi một đối tượng Python có thể gọi được là callable, với số lượng đối số C thay đổi. Các đối số C được mô tả bằng chuỗi định dạng kiểu
Py_BuildValue(). Định dạng có thể là NULL, biểu thị rằng không có đối số nào được cung cấp.Trả về kết quả của cuộc gọi thành công hoặc đưa ra một ngoại lệ và trả về NULL khi thất bại.
Điều này tương đương với biểu thức Python:
callable(*args).Lưu ý rằng nếu bạn chỉ truyền PyObject* args thì
PyObject_CallFunctionObjArgs()là một lựa chọn thay thế nhanh hơn.Thay đổi trong phiên bản 3.4: Loại format đã được thay đổi từ
char *.
-
PyObject *PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...)¶
- Giá trị trả về: Tham chiếu mới. Một phần của ABI ổn định.
Gọi phương thức có tên name của đối tượng obj với số lượng đối số C thay đổi. Các đối số C được mô tả bằng chuỗi định dạng
Py_BuildValue()sẽ tạo ra một bộ dữ liệu.Định dạng có thể là NULL, biểu thị rằng không có đối số nào được cung cấp.
Trả về kết quả của cuộc gọi thành công hoặc đưa ra một ngoại lệ và trả về NULL khi thất bại.
Điều này tương đương với biểu thức Python:
obj.name(arg1, arg2, ...).Lưu ý rằng nếu bạn chỉ truyền PyObject* args thì
PyObject_CallMethodObjArgs()là một lựa chọn thay thế nhanh hơn.Thay đổi trong phiên bản 3.4: Các loại name và format đã được thay đổi từ
char *.
-
PyObject *PyObject_CallFunctionObjArgs(PyObject *callable, ...)¶
- Giá trị trả về: Tham chiếu mới. Một phần của ABI ổn định.
Gọi một đối tượng Python có thể gọi được là callable, với số lượng đối số PyObject* có thể thay đổi. Các đối số được cung cấp dưới dạng số lượng tham số thay đổi theo sau là NULL.
Trả về kết quả của cuộc gọi thành công hoặc đưa ra một ngoại lệ và trả về NULL khi thất bại.
Điều này tương đương với biểu thức Python:
callable(arg1, arg2, ...).
-
PyObject *PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)¶
- Giá trị trả về: Tham chiếu mới. Một phần của ABI ổn định.
Gọi một phương thức của đối tượng Python obj, trong đó tên của phương thức được đặt dưới dạng đối tượng chuỗi Python trong name. Nó được gọi với số lượng đối số PyObject* thay đổi. Các đối số được cung cấp dưới dạng số lượng tham số thay đổi, theo sau là NULL.
Trả về kết quả của cuộc gọi thành công hoặc đưa ra một ngoại lệ và trả về NULL khi thất bại.
-
PyObject *PyObject_CallMethodNoArgs(PyObject *obj, PyObject *name)¶
Gọi một phương thức của đối tượng Python obj mà không có đối số, trong đó tên của phương thức được đặt dưới dạng đối tượng chuỗi Python trong name.
Trả về kết quả của cuộc gọi thành công hoặc đưa ra một ngoại lệ và trả về NULL khi thất bại.
Added in version 3.9.
-
PyObject *PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)¶
Gọi một phương thức của đối tượng Python obj với một đối số vị trí duy nhất arg, trong đó tên của phương thức được đặt dưới dạng đối tượng chuỗi Python trong name.
Trả về kết quả của cuộc gọi thành công hoặc đưa ra một ngoại lệ và trả về NULL khi thất bại.
Added in version 3.9.
-
PyObject *PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)¶
- Một phần của ABI ổn định kể từ phiên bản 3.12.
Gọi một đối tượng Python có thể gọi được callable. Các đối số giống như đối với
vectorcallfunc. Nếu callable hỗ trợ vectorcall, điều này sẽ gọi trực tiếp hàm vectorcall được lưu trữ trong callable.Trả về kết quả của cuộc gọi thành công hoặc đưa ra một ngoại lệ và trả về NULL khi thất bại.
Added in version 3.8: như
_PyObject_VectorcallThay đổi trong phiên bản 3.9: Đã đổi tên thành tên hiện tại, không có dấu gạch dưới ở đầu. Tên tạm thời cũ là soft deprecated.
-
PyObject *PyObject_VectorcallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict)¶
Gọi callable với các đối số vị trí được truyền chính xác như trong giao thức vectorcall, nhưng với các đối số từ khóa được truyền dưới dạng từ điển kwdict. Mảng args chỉ chứa các đối số vị trí.
Bất kể giao thức nào được sử dụng nội bộ, việc chuyển đổi các đối số cần phải được thực hiện. Do đó, chỉ nên sử dụng hàm này nếu người gọi đã có sẵn một từ điển để sử dụng cho các đối số từ khóa chứ không phải một bộ dữ liệu cho các đối số vị trí.
Added in version 3.9.
-
PyObject *PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, size_t nargsf, PyObject *kwnames)¶
- Một phần của ABI ổn định kể từ phiên bản 3.12.
Gọi một phương thức bằng cách sử dụng quy ước gọi vectorcall. Tên của phương thức được đặt dưới dạng chuỗi Python name. Đối tượng có phương thức được gọi là args[0] và mảng args bắt đầu từ args[1] đại diện cho các đối số của lệnh gọi. Phải có ít nhất một đối số vị trí. nargsf là số đối số vị trí bao gồm args[0], cộng với
PY_VECTORCALL_ARGUMENTS_OFFSETnếu giá trị củaargs[0]có thể tạm thời bị thay đổi. Đối số từ khóa có thể được chuyển giống như trongPyObject_Vectorcall().Nếu đối tượng có tính năng
Py_TPFLAGS_METHOD_DESCRIPTOR, đối tượng này sẽ gọi đối tượng phương thức không liên kết với vectơ args đầy đủ làm đối số.Trả về kết quả của cuộc gọi thành công hoặc đưa ra một ngoại lệ và trả về NULL khi thất bại.
Added in version 3.9.
Gọi hỗ trợ API¶
-
int PyCallable_Check(PyObject *o)¶
- Một phần của ABI ổn định.
Xác định xem đối tượng o có thể gọi được hay không. Trả về
1nếu đối tượng có thể gọi được và0nếu không. Chức năng này luôn thành công.