پروتکل فراخوانی

سی‌پایتون از دو پروتکل فراخوانی متفاوت پشتیبانی می‌کند: tp_call و vectorcall.

پروتکل tp_call

نمونه‌های کلاس‌هایی که tp_call را تنظیم می‌کنند، فراخوانی‌پذیر هستند. امضای این جایگاه به صورت زیر است:

PyObject *tp_call(PyObject *callable, PyObject *args, PyObject *kwargs);

فراخوانی با استفاده از یک تاپل برای آرگومان‌های جایگاهی و یک دیکشنری برای آرگومان‌های کلیدواژه‌ای انجام می‌شود، مشابه callable(*args, **kwargs) در کد پایتون. args باید غیر NULL باشد (اگر آرگومانی وجود ندارد، از یک تاپل خالی استفاده کنید) اما kwargs در صورت نبود آرگومان‌های کلیدواژه‌ای می‌تواند NULL باشد.

این قرارداد تنها توسط tp_call استفاده نمی‌شود: tp_new و tp_init نیز آرگومان‌ها را به این روش پاس می‌دهند.

برای فراخوانی یک شیء، از PyObject_Call() یا API فراخوانی دیگری استفاده کنید.

پروتکل Vectorcall

اضافه شده در نسخه‌ی 3.9.

پروتکل vectorcall در PEP 590 به‌عنوان پروتکلی اضافی برای کارآمدتر کردن فراخوانی‌ها معرفی شد.

به عنوان یک قاعده سرانگشتی، سی‌پایتون برای فراخوانی‌های داخلی vectorcall را ترجیح می‌دهد، مشروط به اینکه فراخوانی‌پذیر از آن پشتیبانی کند. با این حال، این یک قاعده سفت و سخت نیست. افزون بر این، برخی افزونه‌های شخص ثالث مستقیماً از tp_call استفاده می‌کنند (به‌جای استفاده از PyObject_Call()). بنابراین، کلاسی که از vectorcall پشتیبانی می‌کند باید tp_call را نیز پیاده‌سازی کند. به‌علاوه، فراخوانی‌پذیر باید فارغ از اینکه از کدام پروتکل استفاده می‌شود، رفتار یکسانی داشته باشد. راه توصیه‌شده برای دستیابی به این هدف، تنظیم tp_call روی PyVectorcall_Call() است. این نکته شایان تکرار است:

هشدار

کلاسی که از vectorcall پشتیبانی می‌کند، باید tp_call را نیز با همان معناشناسی پیاده‌سازی کند.

تغییر یافته در نسخه‌ی 3.12: پرچم Py_TPFLAGS_HAVE_VECTORCALL اکنون هنگامی از یک کلاس حذف می‌شود که متد __call__() آن کلاس دوباره انتساب داده شود. (این کار به‌طور داخلی فقط tp_call را تنظیم می‌کند و در نتیجه ممکن است رفتار آن متفاوت از تابع vectorcall باشد.) در نسخه‌های پیشین پایتون، vectorcall باید فقط با نوع‌های تغییرناپذیر یا ایستا استفاده شود.

یک کلاس نباید vectorcall را پیاده‌سازی کند اگر این کار کندتر از tp_call باشد. برای مثال، اگر فراخوانی‌شونده به هر حال نیاز داشته باشد که آرگومان‌ها را به تاپل args و دیکشنری kwargs تبدیل کند، آنگاه پیاده‌سازی vectorcall فایده‌ای ندارد.

کلاس‌ها می‌توانند پروتکل vectorcall را با فعال کردن پرچم Py_TPFLAGS_HAVE_VECTORCALL و تنظیم tp_vectorcall_offset به آفست درون ساختار شیء که یک vectorcallfunc در آن قرار دارد، پیاده‌سازی کنند. این یک اشاره‌گر به تابعی با امضای زیر است:

typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)
قسمتی از ABI پایدار از نسخه‌ی 3.12.
  • callable شیءی است که فراخوانی می‌شود.

  • args یک آرایه‌ی C است که از آرگومان‌های جایگاهی تشکیل شده و به دنبال آن

    مقادیر آرگومان‌های کلیدواژه‌ای. در صورت نبود آرگومان، این می‌تواند NULL باشد.

  • nargsf تعداد آرگومان‌های جایگاهی است به‌علاوه احتمالاً

    پرچم PY_VECTORCALL_ARGUMENTS_OFFSET. برای به‌دست‌آوردن تعداد واقعی آرگومان‌های جایگاهی از nargsf، از PyVectorcall_NARGS() استفاده کنید.

  • kwnames تاپلی است که نام‌های آرگومان‌های کلیدواژه‌ای را در بر دارد؛

    به عبارت دیگر، کلیدهای دیکشنری kwargs. این نام‌ها باید رشته باشند (نمونه‌هایی از str یا یک زیرکلاس) و باید یکتا باشند. اگر آرگومان کلیدواژه‌ای وجود نداشته باشد، kwnames می‌تواند به‌جای آن NULL باشد.

PY_VECTORCALL_ARGUMENTS_OFFSET
قسمتی از ABI پایدار از نسخه‌ی 3.12.

اگر این پرچم در آرگومان nargsf مربوط به vectorcall تنظیم شده باشد، به فراخوانی‌شونده اجازه داده می‌شود که args[-1] را به‌طور موقت تغییر دهد. به عبارت دیگر، args به آرگومان ۱ (نه ۰) در بردار تخصیص‌یافته اشاره می‌کند. فراخوانی‌شونده باید پیش از بازگشت، مقدار args[-1] را بازگرداند.

برای PyObject_VectorcallMethod()، این پرچم در عوض به این معنا است که args[0] ممکن است تغییر کند.

هرگاه بتوانند این کار را با هزینه‌ی اندکی انجام دهند (بدون تخصیص اضافی)، به فراخوان‌کنندگان توصیه می‌شود که از PY_VECTORCALL_ARGUMENTS_OFFSET استفاده کنند. انجام این کار به فراخوانی‌پذیرهایی مانند متدهای مقید اجازه می‌دهد تا فراخوانی‌های بعدی خود (که آرگومان self در ابتدای آن‌ها افزوده شده است) را به‌طور بسیار کارآمد انجام دهند.

اضافه شده در نسخه‌ی 3.8.

برای فراخوانی شیئی که vectorcall را پیاده‌سازی می‌کند، مانند هر فراخوانی‌پذیر دیگری از یک تابع API فراخوانی استفاده کنید. PyObject_Vectorcall() معمولاً کارآمدترین خواهد بود.

کنترل بازگشت

هنگام استفاده از tp_call، لازم نیست فراخوانی‌شده‌ها نگران بازگشتی باشند: سی‌پایتون برای فراخوانی‌هایی که با استفاده از tp_call انجام می‌شوند، از Py_EnterRecursiveCall() و Py_LeaveRecursiveCall() استفاده می‌کند.

برای کارایی، این موضوع در مورد فراخوانی‌هایی که با استفاده از vectorcall انجام می‌شوند صدق نمی‌کند: فراخوانی‌شونده باید در صورت نیاز از Py_EnterRecursiveCall و Py_LeaveRecursiveCall استفاده کند.

API پشتیبانی Vectorcall

Py_ssize_t PyVectorcall_NARGS(size_t nargsf)
قسمتی از ABI پایدار از نسخه‌ی 3.12.

با دریافت آرگومان nargsf در فراخوانی برداری (vectorcall)، تعداد واقعی آرگومان‌ها را برمی‌گرداند. در حال حاضر معادل است با:

(Py_ssize_t)(nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET)

با این حال، برای پشتیبانی از توسعه‌های آینده، باید از تابع PyVectorcall_NARGS استفاده شود.

اضافه شده در نسخه‌ی 3.8.

vectorcallfunc PyVectorcall_Function(PyObject *op)

اگر op از پروتکل فراخوانی برداری پشتیبانی نکند (چه به این دلیل که نوع آن پشتیبانی نکند و چه به این دلیل که نمونه‌ی خاص پشتیبانی نکند)، مقدار NULL برگردانده می‌شود. در غیر این صورت، اشاره‌گر تابع vectorcall ذخیره‌شده در op برگردانده می‌شود. این تابع هرگز استثنا ایجاد نمی‌کند.

این عمدتاً برای بررسی اینکه آیا op از vectorcall پشتیبانی می‌کند یا خیر مفید است؛ این کار را می‌توان با بررسی PyVectorcall_Function(op) != NULL انجام داد.

اضافه شده در نسخه‌ی 3.9.

PyObject *PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict)
قسمتی از ABI پایدار از نسخه‌ی 3.12.

تابع vectorcallfunc مربوط به callable را با آرگومان‌های جایگاهی و کلیدواژه‌ایِ داده‌شده به‌ترتیب در یک تاپل و یک دیکشنری فراخوانی کنید.

این یک تابع تخصصی است که در نظر گرفته شده است تا در جایگاه tp_call قرار گیرد یا در پیاده‌سازی tp_call استفاده شود. این تابع پرچم Py_TPFLAGS_HAVE_VECTORCALL را بررسی نمی‌کند و به tp_call بازنمی‌گردد.

اضافه شده در نسخه‌ی 3.8.

API فراخوانی شیء

توابع گوناگونی برای فراخوانی یک شیء پایتون در دسترس هستند. هر یک از این توابع، آرگومان‌های خود را به قراردادی که شیء فراخوانی‌شده از آن پشتیبانی می‌کند — یعنی tp_call یا vectorcall — تبدیل می‌کند. برای انجام کمترین تبدیل ممکن، تابعی را انتخاب کنید که بهترین تناسب را با قالب داده‌های در دسترس شما دارد.

جدول زیر خلاصه‌ای از توابع موجود را ارائه می‌دهد؛ برای جزئیات، لطفاً به مستندات هر یک مراجعه کنید.

تابع

فراخوانی‌پذیر

args

kwargs

PyObject_Call()

PyObject *

تاپل

dict/NULL

PyObject_CallNoArgs()

PyObject *

---

---

PyObject_CallOneArg()

PyObject *

۱ شیء

---

PyObject_CallObject()

PyObject *

تاپل/NULL

---

PyObject_CallFunction()

PyObject *

قالب

---

PyObject_CallMethod()

obj + char*

قالب

---

PyObject_CallFunctionObjArgs()

PyObject *

variadic

---

PyObject_CallMethodObjArgs()

obj + name

variadic

---

PyObject_CallMethodNoArgs()

obj + name

---

---

PyObject_CallMethodOneArg()

obj + name

۱ شیء

---

PyObject_Vectorcall()

PyObject *

فراخوانی برداری

فراخوانی برداری

PyObject_VectorcallDict()

PyObject *

فراخوانی برداری

dict/NULL

PyObject_VectorcallMethod()

arg + name

فراخوانی برداری

فراخوانی برداری

PyObject *PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
مقدار بازگشتی: مرجع جدید. قسمتی از ABI پایدار.

شیء فراخوانی‌پذیر پایتون callable را با آرگومان‌هایی که توسط تاپل args داده شده‌اند و آرگومان‌های نام‌داری که توسط دیکشنری kwargs داده شده‌اند، فراخوانی می‌کند.

args نباید NULL باشد؛ اگر هیچ آرگومانی لازم نیست، از یک تاپل خالی استفاده کنید. اگر هیچ آرگومان نام‌داری لازم نیست، kwargs می‌تواند NULL باشد.

در صورت موفقیت، نتیجه‌ی فراخوانی را برمی‌گرداند، یا در صورت شکست، استثنا ایجاد کرده و NULL را برمی‌گرداند.

این معادل عبارت پایتونی callable(*args, **kwargs) است.

PyObject *PyObject_CallNoArgs(PyObject *callable)
مقدار بازگشتی: مرجع جدید. قسمتی از ABI پایدار از نسخه‌ی 3.10.

یک شیء پایتونی فراخوانی‌پذیر callable را بدون هیچ آرگومانی فراخوانی می‌کند. این کارآمدترین راه برای فراخوانی یک شیء پایتونی فراخوانی‌پذیر بدون هیچ آرگومانی است.

در صورت موفقیت، نتیجه‌ی فراخوانی را برمی‌گرداند، یا در صورت شکست، استثنا ایجاد کرده و NULL را برمی‌گرداند.

اضافه شده در نسخه‌ی 3.9.

PyObject *PyObject_CallOneArg(PyObject *callable, PyObject *arg)
مقدار بازگشتی: مرجع جدید.

یک شیء فراخوانی‌پذیر پایتون callable را با دقیقاً ۱ آرگومان جایگاهی arg و بدون هیچ آرگومان کلیدواژه‌ای فراخوانی می‌کند.

در صورت موفقیت، نتیجه‌ی فراخوانی را برمی‌گرداند، یا در صورت شکست، استثنا ایجاد کرده و NULL را برمی‌گرداند.

اضافه شده در نسخه‌ی 3.9.

PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)
مقدار بازگشتی: مرجع جدید. قسمتی از ABI پایدار.

شیء فراخوانی‌پذیر پایتون callable را با آرگومان‌های داده‌شده توسط تاپل args فراخوانی می‌کند. اگر نیازی به آرگومان نباشد، args می‌تواند NULL باشد.

در صورت موفقیت، نتیجه‌ی فراخوانی را برمی‌گرداند، یا در صورت شکست، استثنا ایجاد کرده و NULL را برمی‌گرداند.

این معادل عبارت زیر در پایتون است: callable(*args).

PyObject *PyObject_CallFunction(PyObject *callable, const char *format, ...)
مقدار بازگشتی: مرجع جدید. قسمتی از ABI پایدار.

شیء پایتون فراخوانی‌پذیر callable را با تعداد متغیری از آرگومان‌های C فراخوانی می‌کند. آرگومان‌های C با استفاده از رشته قالب به سبک Py_BuildValue() توصیف می‌شوند. قالب می‌تواند NULL باشد که نشان می‌دهد هیچ آرگومانی ارائه نشده است.

در صورت موفقیت، نتیجه‌ی فراخوانی را برمی‌گرداند، یا در صورت شکست، استثنا ایجاد کرده و NULL را برمی‌گرداند.

این معادل عبارت زیر در پایتون است: callable(*args).

توجه داشته باشید که اگر فقط آرگومان‌های PyObject* را ارسال کنید، PyObject_CallFunctionObjArgs() جایگزین سریع‌تری است.

تغییر یافته در نسخه‌ی 3.4: نوع format از char * تغییر کرد.

PyObject *PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...)
مقدار بازگشتی: مرجع جدید. قسمتی از ABI پایدار.

متدی با نام name از شیء obj را با تعداد متغیری از آرگومان‌های C فراخوانی می‌کند. آرگومان‌های C توسط یک رشته قالب Py_BuildValue() توصیف می‌شوند که باید یک تاپل تولید کند.

قالب می‌تواند NULL باشد که نشان می‌دهد هیچ آرگومانی ارائه نمی‌شود.

در صورت موفقیت، نتیجه‌ی فراخوانی را برمی‌گرداند، یا در صورت شکست، استثنا ایجاد کرده و NULL را برمی‌گرداند.

این معادل عبارت پایتون obj.name(arg1, arg2, ...) است.

توجه داشته باشید که اگر تنها آرگومان‌هایی از نوع PyObject* را عبور می‌دهید، PyObject_CallMethodObjArgs() جایگزین سریع‌تری است.

تغییر یافته در نسخه‌ی 3.4: نوع‌های name و format از char * تغییر یافتند.

PyObject *PyObject_CallFunctionObjArgs(PyObject *callable, ...)
مقدار بازگشتی: مرجع جدید. قسمتی از ABI پایدار.

یک شیء پایتونی فراخوانی‌پذیر callable را با تعداد متغیری آرگومان PyObject* فراخوانی می‌کند. آرگومان‌ها به‌صورت تعداد متغیری پارامتر و به‌دنبال آن NULL ارائه می‌شوند.

در صورت موفقیت، نتیجه‌ی فراخوانی را برمی‌گرداند، یا در صورت شکست، استثنا ایجاد کرده و NULL را برمی‌گرداند.

این معادل عبارت پایتون callable(arg1, arg2, ...) است.

PyObject *PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)
مقدار بازگشتی: مرجع جدید. قسمتی از ABI پایدار.

متدی از شیء پایتونی obj را فراخوانی می‌کند که نام آن به‌صورت یک شیء رشته پایتون در name داده شده است. این متد با تعداد متغیری از آرگومان‌های PyObject* فراخوانی می‌شود. آرگومان‌ها به‌صورت تعداد متغیری از پارامترها و در پایان NULL ارائه می‌شوند.

در صورت موفقیت، نتیجه‌ی فراخوانی را برمی‌گرداند، یا در صورت شکست، استثنا ایجاد کرده و NULL را برمی‌گرداند.

PyObject *PyObject_CallMethodNoArgs(PyObject *obj, PyObject *name)

متدی از شیء پایتون obj را بدون آرگومان فراخوانی می‌کند، که در آن نام متد به‌صورت یک شیء رشته پایتون در name داده شده است.

در صورت موفقیت، نتیجه‌ی فراخوانی را برمی‌گرداند، یا در صورت شکست، استثنا ایجاد کرده و NULL را برمی‌گرداند.

اضافه شده در نسخه‌ی 3.9.

PyObject *PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)

متدی از شیء پایتون obj را با تنها یک آرگومان جایگاهی arg فراخوانی می‌کند، که نام متد به‌صورت یک شیء رشته پایتون در name داده می‌شود.

در صورت موفقیت، نتیجه‌ی فراخوانی را برمی‌گرداند، یا در صورت شکست، استثنا ایجاد کرده و NULL را برمی‌گرداند.

اضافه شده در نسخه‌ی 3.9.

PyObject *PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)
قسمتی از ABI پایدار از نسخه‌ی 3.12.

شیء پایتونی فراخوانی‌پذیر callable را فراخوانی می‌کند. آرگومان‌ها همانند آرگومان‌های vectorcallfunc هستند. اگر callable از vectorcall پشتیبانی کند، این تابع مستقیماً تابع vectorcall ذخیره‌شده در callable را فراخوانی می‌کند.

در صورت موفقیت، نتیجه‌ی فراخوانی را برمی‌گرداند، یا در صورت شکست، استثنا ایجاد کرده و NULL را برمی‌گرداند.

اضافه شده در نسخه‌ی 3.8: به‌عنوان _PyObject_Vectorcall

تغییر یافته در نسخه‌ی 3.9: به نام کنونی تغییر نام داده شد، بدون زیرخط ابتدایی. نام آزمایشی قبلی soft deprecated است.

PyObject *PyObject_VectorcallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict)

فراخوانی‌پذیر callable را با آرگومان‌های جایگاهی که دقیقاً مطابق پروتکل vectorcall ارسال می‌شوند فراخوانی می‌کند، اما آرگومان‌های کلیدواژه‌ای به‌صورت دیکشنری kwdict ارسال می‌شوند. آرایه args فقط شامل آرگومان‌های جایگاهی است.

صرف‌نظر از اینکه کدام پروتکل به‌طور داخلی استفاده می‌شود، باید تبدیلی روی آرگومان‌ها انجام شود. بنابراین، این تابع تنها زمانی باید استفاده شود که فراخوان‌کننده از قبل دیکشنری آماده‌ای برای آرگومان‌های کلیدواژه‌ای داشته باشد، اما تاپلی برای آرگومان‌های جایگاهی نداشته باشد.

اضافه شده در نسخه‌ی 3.9.

PyObject *PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, size_t nargsf, PyObject *kwnames)
قسمتی از ABI پایدار از نسخه‌ی 3.12.

یک متد را با استفاده از قرارداد فراخوانی vectorcall فراخوانی کنید. نام متد به صورت رشته پایتون name داده می‌شود. شیئی که متد آن فراخوانی می‌شود args[0] است و آرایه args که از args[1] آغاز می‌شود، آرگومان‌های فراخوانی را نشان می‌دهد. باید حداقل یک آرگومان جایگاهی وجود داشته باشد. nargsf تعداد آرگومان‌های جایگاهی شامل args[0] است، به‌علاوه PY_VECTORCALL_ARGUMENTS_OFFSET در صورتی که مقدار args[0] ممکن است به‌طور موقت تغییر کند. آرگومان‌های کلیدواژه‌ای را می‌توان دقیقاً مشابه PyObject_Vectorcall() ارسال کرد.

اگر شیء قابلیت Py_TPFLAGS_METHOD_DESCRIPTOR را داشته باشد، این، شیء متد غیرمقید را با بردار کامل args به‌عنوان آرگومان‌ها فراخوانی می‌کند.

در صورت موفقیت، نتیجه‌ی فراخوانی را برمی‌گرداند، یا در صورت شکست، استثنا ایجاد کرده و NULL را برمی‌گرداند.

اضافه شده در نسخه‌ی 3.9.

API پشتیبانی فراخوانی

int PyCallable_Check(PyObject *o)
قسمتی از ABI پایدار.

تعیین می‌کند که آیا شیء o فراخوانی‌پذیر است یا خیر. اگر شیء فراخوانی‌پذیر باشد 1 و در غیر این صورت 0 را برمی‌گرداند. این تابع همیشه با موفقیت اجرا می‌شود.