مقدمه¶
رابط برنامهنویسی کاربردی پایتون، دسترسی به مفسر پایتون را در سطوح گوناگون در اختیار برنامهنویسان C و C++ قرار میدهد. این API از C++ نیز به همان اندازه قابل استفاده است، اما برای اختصار، معمولاً از آن با عنوان Python/C API یاد میشود. دو دلیل اساساً متفاوت برای استفاده از Python/C API وجود دارد. دلیل اول، نوشتن ماژولهای توسعهای برای مقاصد خاص است؛ اینها ماژولهای C هستند که مفسر پایتون را توسعه میدهند. این احتمالاً رایجترین کاربرد است. دلیل دوم، استفاده از پایتون بهعنوان یک جزء در یک برنامه بزرگتر است؛ به این تکنیک عموماً تعبیه <embedding> پایتون در یک برنامه گفته میشود.
نوشتن یک ماژول توسعهای فرایندی نسبتاً شناختهشده است که در آن رویکرد «کتاب آشپزی» بهخوبی کار میکند. چندین ابزار وجود دارند که این فرایند را تا حدی خودکار میکنند. هرچند مردم از همان اوایل پیدایش پایتون، آن را در برنامههای دیگر تعبیه کردهاند، اما فرایند تعبیه پایتون نسبت به نوشتن یک ماژول توسعهای کمتر سرراست است.
بسیاری از توابع API صرفنظر از اینکه در حال تعبیه کردن یا توسعه دادن پایتون هستید، مفید هستند؛ علاوه بر این، بیشتر برنامههایی که پایتون را تعبیه میکنند، به ارائه یک توسعه سفارشی نیز نیاز خواهند داشت، بنابراین احتمالاً ایده خوبی است که پیش از تلاش برای تعبیه پایتون در یک برنامه واقعی، با نوشتن یک توسعه آشنا شوید.
سازگاری نسخههای زبان¶
API زبان C پایتون با نسخههای C11 و C++11 از زبانهای C و C++ سازگار است.
این یک حد پایین است: API زبان C به ویژگیهای نسخههای بعدی C/C++ نیازی ندارد. شما نیازی ندارید که حالت "c11" کامپایلر خود را فعال کنید.
استانداردهای کدنویسی¶
اگر در حال نوشتن کد C برای گنجاندن در سیپایتون هستید، باید از دستورالعملها و استانداردهای تعریفشده در PEP 7 پیروی کنید. این دستورالعملها صرفنظر از نسخهی پایتونی که در آن مشارکت میکنید، اعمال میشوند. پیروی از این قراردادها برای ماژولهای توسعهای شخص ثالث خودتان ضروری نیست، مگر آنکه در نهایت انتظار داشته باشید آنها را به پایتون مشارکت دهید.
پروندههای include¶
تمام تعریفهای تابع، نوع و ماکروی مورد نیاز برای استفاده از Python/C API، با سطر زیر در کد شما گنجانده میشوند:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
این به معنای گنجاندهشدن سرآیندهای استاندارد زیر است: <stdio.h>، <string.h>، <errno.h>، <limits.h>، <assert.h> و <stdlib.h> (در صورت وجود).
توجه
از آنجا که پایتون ممکن است برخی تعریفهای پیشپردازنده را تعریف کند که در برخی سیستمها بر سرآیندهای استاندارد تأثیر میگذارند، شما باید پیش از گنجاندن هر سرآیند استانداردی، Python.h را بگنجانید.
توصیه میشود که همیشه PY_SSIZE_T_CLEAN را پیش از گنجاندن Python.h تعریف کنید. برای توضیح این ماکرو، تجزیه آرگومانها و ساخت مقادیر را ببینید.
همهی نامهای قابل مشاهده برای کاربر که توسط Python.h تعریف شدهاند (بهجز نامهایی که توسط سرآیندهای استانداردِ گنجاندهشده تعریف شدهاند) یکی از پیشوندهای Py یا _Py را دارند. نامهایی که با _Py آغاز میشوند برای استفادهی داخلی پیادهسازی پایتون هستند و نباید توسط نویسندگان ماژولهای توسعهای استفاده شوند. نامهای اعضای ساختار پیشوند رزروشدهای ندارند.
توجه
کد کاربر هرگز نباید نامهایی تعریف کند که با Py یا _Py آغاز میشوند. این امر خواننده را سردرگم میکند و قابلیت حمل کد کاربر به نسخههای آینده پایتون را به خطر میاندازد؛ چرا که این نسخهها ممکن است نامهای دیگری تعریف کنند که با یکی از این پیشوندها آغاز میشوند.
پروندههای سرآیند معمولاً همراه با پایتون نصب میشوند. در یونیکس، این پروندهها در پوشههای prefix/include/pythonversion/ و exec_prefix/include/pythonversion/ قرار دارند؛ جایی که prefix و exec_prefix توسط پارامترهای متناظرِ اسکریپت configure پایتون تعریف میشوند و نسخه برابر '%d.%d' % sys.version_info[:2] است. در ویندوز، سرآیندها در prefix/include نصب میشوند که در آن prefix پوشهی نصب مشخصشده به نصبکننده است.
برای گنجاندن سرآیندها، هر دو پوشه (در صورت تفاوت) را در مسیر جستجوی کامپایلر خودتان برای سرآیندها قرار دهید. پوشههای والد را در مسیر جستجو قرار داده و سپس از #include <pythonX.Y/Python.h> استفاده نکنید؛ این کار در ساختهای چندسکویی خراب میشود، زیرا سرآیندهای مستقل از سکو که در prefix قرار دارند، سرآیندهای وابسته به سکو از exec_prefix را دربر میگیرند.
کاربران C++ باید توجه داشته باشند که اگرچه API کاملاً با استفاده از C تعریف شده است، پروندههای سرآیند نقاط ورود را بهدرستی بهصورت extern "C" اعلام میکنند. در نتیجه، برای استفاده از API در C++ نیازی به انجام کار خاصی نیست.
ماکروهای مفید¶
چندین ماکروی مفید در پروندههای سرآیند پایتون تعریف شدهاند. بسیاری از آنها نزدیکتر به جایی که به کار میآیند تعریف شدهاند (برای مثال، Py_RETURN_NONE و PyMODINIT_FUNC). ماکروهای دیگر که کاربرد عمومیتری دارند، در اینجا تعریف شدهاند. این فهرست لزوماً کامل نیست.
-
Py_CAN_START_THREADS¶
اگر این ماکرو تعریفشده باشد، سیستم فعلی میتواند نخها را آغاز کند.
در حال حاضر، همه سیستمهای پشتیبانیشده توسط سیپایتون (طبق PEP 11)، به استثنای برخی پلتفرمهای WebAssembly، از راهاندازی نخها پشتیبانی میکنند.
اضافه شده در نسخهی 3.13.
-
Py_GETENV(s)¶
مانند
getenv(s)، اما اگر-Eدر خط فرمان گذرانده شده باشد،NULLبرمیگرداند (نگاه کنید بهPyConfig.use_environment).
ماکروهای رشته مستند¶
-
PyDoc_STRVAR(name, str)¶
یک متغیر با نام name ایجاد میکند که میتوان از آن در رشتههای مستند استفاده کرد. اگر پایتون بدون رشتههای مستند ساخته شود (
--without-doc-strings)، مقدار آن یک رشته خالی خواهد بود.مثال:
PyDoc_STRVAR(pop_doc, "راستترین عنصر را حذف کرده و برمیگرداند."); static PyMethodDef deque_methods[] = { // ... {"pop", (PyCFunction)deque_pop, METH_NOARGS, pop_doc}, // ... }
به
PyDoc_VAR(name) = PyDoc_STR(str)بسط مییابد.
-
PyDoc_STR(str)¶
به رشته ورودی دادهشده بسط مییابد، یا اگر رشتههای مستند غیرفعال باشند (
--without-doc-strings)، به رشته خالی.مثال:
static PyMethodDef pysqlite_row_methods[] = { {"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS, PyDoc_STR("Returns the keys of the row.")}, {NULL, NULL} };
-
PyDoc_VAR(name)¶
یک متغیر آرایهی نویسهای ایستا با نام دادهشده اعلان میکند. به
static const char name[]بسط مییابدبرای مثال:
PyDoc_VAR(python_doc) = PyDoc_STR( "سردهای از مارهای فشارنده در خانوادهی Pythonidae بومی " "مناطق حاره و نیمهحارهی نیمکره شرقی.");
ماکروهای کاربردی عمومی¶
ماکروهای زیر برای کارهای رایجی هستند که مخصوص پایتون نیستند.
-
Py_UNUSED(arg)¶
از این برای آرگومانهای استفادهنشده در تعریف تابع استفاده کنید تا هشدارهای کامپایلر سرکوب شوند. مثال:
int func(int a, int Py_UNUSED(b)) { return a; }.اضافه شده در نسخهی 3.4.
-
Py_GCC_ATTRIBUTE(name)¶
از صفت GCC با نام name استفاده کنید تا از کامپایلرهایی که صفات GCC را پشتیبانی نمیکنند (مانند MSVC) پنهان بماند.
این در کامپایلر GCC به
__attribute__((name))بسط مییابد و در کامپایلرهایی که از ویژگیهای GCC پشتیبانی نمیکنند، به هیچچیز بسط نمییابد.
ابزارهای عددی¶
-
Py_ABS(x)¶
مقدار مطلق
xرا برمیگرداند.آرگومان ممکن است بیش از یک بار ارزیابی شود. در نتیجه، عبارتی با اثر جانبی را مستقیماً به این ماکرو ندهید.
اگر نتوان نتیجه را نمایش داد (برای مثال، اگر
xبرای نوع int مقدارINT_MINداشته باشد)، رفتار تعریفنشده است.تقریباً معادل
((x) < 0 ? -(x) : (x))استاضافه شده در نسخهی 3.3.
-
Py_MAX(x, y)¶
-
Py_MIN(x, y)¶
بازگرداندن بزرگتر یا کوچکتر آرگومانها، به ترتیب.
ممکن است هر یک از آرگومانها بیش از یک بار ارزیابی شود. در نتیجه، عبارتی با اثرات جانبی را مستقیماً به این ماکرو پاس ندهید.
Py_MAXتقریباً معادل(((x) > (y)) ? (x) : (y))است.اضافه شده در نسخهی 3.3.
-
Py_ARITHMETIC_RIGHT_SHIFT(type, integer, positions)¶
مشابه
integer >> positionsاست، اما گسترش علامت را اجباری میکند، زیرا استاندارد C تعریف نمیکند که آیا شیفت به راست یک عدد صحیح علامتدار، گسترش علامت انجام میدهد یا با صفر پر میکند.integer میتواند از هر نوع عدد صحیح علامتدار باشد. positions تعداد جایگاههایی است که باید به سمت راست شیفت شوند.
هر دو integer و positions ممکن است بیش از یک بار ارزیابی شوند؛ در نتیجه، از عبور دادن مستقیم یک فراخوانی تابع یا هر عملیات دیگری با عوارض جانبی به این ماکرو خودداری کنید. در عوض، نتیجه را در یک متغیر ذخیره کنید و سپس آن را عبور دهید.
type استفاده نمیشود و تنها برای سازگاری با نسخههای پیشین نگه داشته شده است. در گذشته، type برای تبدیل نوع integer استفاده میشد.
تغییر یافته در نسخهی 3.1: این ماکرو اکنون برای همهی نوعهای عدد صحیح علامتدار معتبر است، نه فقط برای نوعهایی که
unsigned typeبرایشان مجاز است. در نتیجه، type دیگر استفاده نمیشود.
-
Py_CHARMASK(c)¶
آرگومان باید یک نویسه یا یک عدد صحیح در محدودهی [-128, 127] یا [0, 255] باشد. این ماکرو
cرا پس از تبدیل نوع بهunsigned charبرمیگرداند.
ابزارهای ادعا¶
-
Py_UNREACHABLE()¶
از این زمانی استفاده کنید که مسیر کدی دارید که بر اساس طراحی هرگز نمیتوان به آن رسید. برای مثال، در بند
default:در یک دستورswitchکه تمام مقادیر ممکن آن در دستورهایcaseپوشش داده شدهاند. از این در جاهایی استفاده کنید که ممکن است وسوسه شوید که فراخوانیassert(0)یاabort()قرار دهید.در حالت انتشار، این ماکرو به کامپایلر کمک میکند تا کد را بهینهسازی کند و از هشدار دربارهی کد غیرقابلدسترسی جلوگیری میکند. برای مثال، این ماکرو در حالت انتشار روی GCC با
__builtin_unreachable()پیادهسازی شده است.در حالت اشکالزدایی و روی کامپایلرهای پشتیبانینشده، ماکرو به فراخوانی
Py_FatalError()بسط مییابد.یکی از کاربردهای
Py_UNREACHABLE()استفاده از آن پس از فراخوانی تابعی است که هرگز باز نمیگردد اما با_Noreturnاعلان نشده است.اگر مسیری از کد بسیار نامحتمل باشد اما در حالتهای استثنایی بتوان به آن رسید، نباید از این ماکرو استفاده کرد. برای مثال، در وضعیت کمبود حافظه یا زمانی که یک فراخوانی سیستمی مقداری خارج از محدودهی مورد انتظار برمیگرداند. در این حالت، بهتر است خطا به فراخواننده گزارش شود. اگر نتوان خطا را به فراخواننده گزارش داد، میتوان از
Py_FatalError()استفاده کرد.اضافه شده در نسخهی 3.7.
-
Py_SAFE_DOWNCAST(value, larger, smaller)¶
value را از نوع larger به نوع smaller قالبریزی میکند و اعتبارسنجی میکند که هیچ اطلاعاتی از دست نرفته باشد.
در ساختهای انتشار پایتون، این تقریباً معادل
((smaller) value)است (در C++، بهجای آن ازstatic_cast<smaller>(value)استفاده میشود).در ساختهای اشکالزدایی (به این معنا که
Py_DEBUGتعریف شده است)، این ادعا میکند که با تبدیل نوع از بزرگتر به کوچکتر، هیچ اطلاعاتی از دست نرفته است.value، larger و smaller ممکن است هر سه بیش از یک بار در عبارت ارزیابی شوند؛ در نتیجه، عبارتی با عوارض جانبی را مستقیماً به این ماکرو نگذرانید.
-
Py_BUILD_ASSERT(cond)¶
یک شرط زمان کامپایل cond را بهصورت یک دستور ادعا میکند. اگر شرط نادرست باشد یا نتوان آن را در زمان کامپایل ارزیابی کرد، ساخت با شکست مواجه خواهد شد.
تقریباً معادل
static_assert(cond)در C23 و بالاتر است.برای مثال:
Py_BUILD_ASSERT(sizeof(PyTime_t) == sizeof(int64_t));
اضافه شده در نسخهی 3.3.
-
Py_BUILD_ASSERT_EXPR(cond)¶
یک شرط زمان کامپایل cond را بهعنوان عبارتی که به
0ارزیابی میشود، ادعا میکند. اگر شرط نادرست باشد یا نتوان آن را در زمان کامپایل ارزیابی کرد، ساخت شکست میخورد.برای مثال:
#define foo_to_char(foo) \ ((char *)(foo) + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0))
اضافه شده در نسخهی 3.3.
ابزارهای اندازهی نوع¶
-
Py_ARRAY_LENGTH(array)¶
طول یک آرایهی C با تخصیص ایستا را در زمان کامپایل محاسبه میکند.
آرگومان array باید یک آرایهی C با اندازهی معلوم در زمان کامپایل باشد. پاس دادن آرایهای با اندازهی نامعلوم، مانند آرایهی تخصیصیافته در هیپ، در برخی کامپایلرها منجر به خطای کامپایل میشود یا در غیر این صورت، نتایج نادرستی تولید میکند.
این تقریباً معادل است با:
sizeof(array) / sizeof((array)[0])
-
Py_MEMBER_SIZE(type, member)¶
اندازهی یک عضو ساختار (نوع) را به بایت برمیگرداند.
تقریباً معادل
sizeof(((type *)NULL)->member)است.اضافه شده در نسخهی 3.6.
ابزارهای تعریف ماکرو¶
-
Py_FORCE_EXPANSION(X)¶
این معادل
Xاست که برای چسباندن توکنها (token-pasting) در ماکروها مفید است، زیرا بسطهای ماکرو در X بهاجبار توسط پیشپردازنده ارزیابی میشوند.
-
Py_STRINGIFY(x)¶
تبدیل
xبه یک رشتهی C. برای مثال،Py_STRINGIFY(123)مقدار"123"را برمیگرداند.اضافه شده در نسخهی 3.4.
ابزارهای اعلان¶
از ماکروهای زیر میتوان در اعلانها استفاده کرد. این ماکروها بیشترین کاربرد را در تعریف خودِ C API دارند و برای نویسندگان ماژولهای توسعهای کاربرد محدودی دارند. بیشتر آنها به نوشتارهای مخصوص کامپایلر برای توسعههای رایج زبان C بسط مییابند.
-
Py_ALWAYS_INLINE¶
از کامپایلر میخواهد که یک تابع ایستای درونخطی (static inline) را همیشه درونخطی کند. کامپایلر میتواند آن را نادیده بگیرد و تصمیم بگیرد که تابع را درونخطی نکند.
معادل ویژگی
always_inlineدر GCC و__forceinlineدر MSVC است.از آن میتوان برای درونخطی کردن توابع static inline حیاتی از نظر کارایی استفاده کرد، زمانی که پایتون در حالت اشکالزدایی و با درونخطیسازی توابع غیرفعال ساخته میشود. برای مثال، MSC هنگام ساخت در حالت اشکالزدایی، درونخطیسازی توابع را غیرفعال میکند.
علامتگذاری کورکورانهی یک تابع ایستای درونخطی (static inline) با Py_ALWAYS_INLINE میتواند به عملکرد بدتری منجر شود (برای مثال، به دلیل افزایش حجم کد). کامپایلر معمولاً در تحلیل هزینه/فایده از توسعهدهنده هوشمندتر است.
اگر پایتون در حالت اشکالزدایی ساختهشده باشد (اگر ماکروی
Py_DEBUGتعریفشده باشد)، ماکرویPy_ALWAYS_INLINEهیچ کاری انجام نمیدهد.آن باید پیش از نوع بازگشتی تابع مشخص شود. کاربرد:
static inline Py_ALWAYS_INLINE int random(void) { return 4; }
اضافه شده در نسخهی 3.11.
-
Py_NO_INLINE¶
درونخطیسازی (inlining) را روی یک تابع غیرفعال میکند. برای مثال، مصرف پشتهی C را کاهش میدهد: در ساختهای LTO+PGO که کد را بهشدت درونخطی میکنند، مفید است (نگاه کنید به bpo-33720).
معادل ویژگی/مشخصهی
noinlineدر GCC و MSVC است.کاربرد:
Py_NO_INLINE static int random(void) { return 4; }
اضافه شده در نسخهی 3.11.
-
Py_DEPRECATED(version)¶
از این برای اعلام APIهایی که در نسخهی مشخصی از سیپایتون منسوخ شدهاند، استفاده کنید. ماکرو باید پیش از نام نماد قرار گیرد.
مثال:
Py_DEPRECATED(3.8) PyAPI_FUNC(int) Py_OldFunction(void);
تغییر یافته در نسخهی 3.8: پشتیبانی از MSVC اضافه شد.
-
Py_LOCAL(type)¶
برای توابعی که محلی به پرونده جاری هستند، تابعی را که نوع مشخصشده را برمیگرداند، با استفاده از مشخصکننده فراخوانی سریع (fast-calling qualifier) اعلان میکند. از نظر معنایی، این معادل
static typeاست.
-
Py_LOCAL_INLINE(type)¶
معادل
Py_LOCALاست، اما علاوه بر آن درخواست میکند که تابع بهصورت درونخطی شود.
-
Py_LOCAL_SYMBOL¶
ماکرویی که برای اعلام نماد بهعنوان محلیِ کتابخانه اشتراکی (مخفی) استفاده میشود. در پلتفرمهای پشتیبانیشده، تضمین میکند که نماد اکسپورت نشود.
در نسخههای سازگار GCC/Clang، این به
__attribute__((visibility("hidden")))بسط مییابد.
-
Py_EXPORTED_SYMBOL¶
ماکرویی که برای اعلام یک نماد (تابع یا داده) بهعنوان اکسپورتشده به کار میرود. در ویندوز، این ماکرو به
__declspec(dllexport)بسط مییابد. در نسخههای سازگار GCC/Clang، به__attribute__((visibility("default")))بسط مییابد. این ماکرو برای تعریف خودِ C API است؛ ماژولهای توسعهای نباید از آن استفاده کنند.
-
Py_IMPORTED_SYMBOL¶
ماکرویی که برای اعلام یک نماد بهعنوان ایمپورتشده به کار میرود. در ویندوز، این ماکرو به
__declspec(dllimport)بسط مییابد. این ماکرو برای تعریف خودِ C API است؛ ماژولهای توسعهای نباید از آن استفاده کنند.
-
PyAPI_FUNC(type)¶
ماکرویی که توسط سیپایتون برای اعلان یک تابع بهعنوان بخشی از C API استفاده میشود. بسط آن به پلتفرم و پیکربندی ساخت بستگی دارد. این ماکرو برای تعریف خودِ C API سیپایتون در نظر گرفتهشده است؛ ماژولهای توسعهای نباید از آن برای نمادهای خودشان استفاده کنند.
-
PyAPI_DATA(type)¶
ماکرویی که توسط سیپایتون برای اعلان یک متغیر سراسری عمومی بهعنوان بخشی از C API استفاده میشود. بسط آن به پلتفرم و پیکربندی ساخت بستگی دارد. این ماکرو برای تعریف خودِ C API سیپایتون در نظر گرفته شده است؛ ماژولهای توسعهای نباید از آن برای نمادهای خودشان استفاده کنند.
ماکروهای منسوخ¶
از ماکروهای زیر برای ویژگیهایی که در C11 استاندارد شدهاند استفاده شده است.
-
Py_ALIGNED(num)¶
در کامپایلرهایی که از آن پشتیبانی میکنند، همترازی را به num بایت مشخص کنید.
استفاده از مشخصکنندهی
_Alignasاستاندارد C11 را به جای این ماکرو در نظر بگیرید.
-
Py_LL(number)¶
-
Py_ULL(number)¶
از number به ترتیب به عنوان لفظی عدد صحیح
long longیاunsigned long longاستفاده کنید.به number و به ترتیب پس از آن
LLیاLLUگسترش مییابد، اما در برخی کامپایلرهای قدیمیتر به برخی پسوندهای خاص کامپایلر گسترش مییابد.در نظر بگیرید که مستقیماً از پسوندهای استاندارد C99 یعنی
LLوLLUاستفاده کنید.
-
Py_MEMCPY(dest, src, n)¶
این یک نام مستعار برای
memcpy()است.منسوخسازی نرم <Soft deprecated> از نسخهی 3.14: بهجای آن، مستقیماً از
memcpy()استفاده کنید.
-
Py_VA_COPY¶
این یک ناممستعار برای تابع
va_copyمطابق با استاندارد C99 است.از نظر تاریخی، این کار از روشی وابسته به کامپایلر برای کپی کردن
va_listاستفاده میکرد.تغییر یافته در نسخهی 3.6: این اکنون یک نام مستعار برای
va_copyاست.منسوخسازی نرم <Soft deprecated> از نسخهی 3.14.
شیءها، نوعها و شمارش ارجاع¶
بیشتر توابع Python/C API یک یا چند آرگومان و همچنین مقدار بازگشتیای از نوع PyObject* دارند. این نوع، اشارهگر به یک نوع دادهی مبهم است که نمایانگر یک شیء پایتونی دلخواه است. از آنجا که زبان پایتون در بیشتر موقعیتها (مانند انتسابها، قواعد محدوده و ارسال آرگومان) با همهی نوعهای شیء پایتون به یک شکل رفتار میکند، بجاست که همهی آنها با یک نوع C واحد نمایش داده شوند. تقریباً همهی اشیای پایتون روی هیپ قرار دارند: شما هرگز متغیر خودکار یا ایستایی از نوع PyObject تعریف نمیکنید؛ تنها متغیرهای اشارهگر از نوع PyObject* را میتوان تعریف کرد. تنها استثنا اشیای نوع هستند؛ از آنجا که این اشیا هرگز نباید آزادسازی شوند، معمولاً اشیای ایستای PyTypeObject هستند.
همهی اشیاء پایتون (حتی اعداد صحیح پایتون) یک نوع <type> و یک شمارش ارجاع <reference count> دارند. نوع یک شیء تعیین میکند که آن شیء از چه نوعی است (مثلاً یک عدد صحیح، یک فهرست یا یک تابع تعریفشده توسط کاربر؛ انواع بسیار بیشتری نیز وجود دارند که در سلسلهمراتب انواع استاندارد توضیح دادهشدهاند). برای هر یک از انواع شناختهشده، یک ماکرو وجود دارد که بررسی میکند آیا یک شیء از آن نوع است یا خیر؛ برای مثال، PyList_Check(a) درست است اگر (و تنها اگر) شیئی که a به آن اشاره میکند، یک فهرست پایتون باشد.
شمارش ارجاع¶
شمارش ارجاع مهم است زیرا رایانههای امروزی اندازهی حافظهی محدودی (و اغلب بهشدت محدود) دارند؛ این شمارش، تعداد مکانهای مختلفی را میشمارد که ارجاع قوی به یک شیء دارند. چنین مکانی میتواند یک شیء دیگر، یا یک متغیر سراسری (یا ایستا) در C، یا یک متغیر محلی در تابعی به زبان C باشد. وقتی آخرین ارجاع قوی به یک شیء آزاد شود (یعنی شمارش ارجاع آن به صفر برسد)، آن شیء تخصیصزدایی میشود. اگر آن شیء شامل ارجاعهایی به اشیاء دیگر باشد، آن ارجاعها آزاد میشوند. آن اشیاء دیگر نیز ممکن است به نوبت خود تخصیصزدایی شوند، اگر ارجاع دیگری به آنها وجود نداشته باشد، و به همین ترتیب. (در اینجا یک مشکل آشکار با اشیائی که به یکدیگر ارجاع دارند وجود دارد؛ فعلاً راهحل این است: «این کار را نکنید.»)
شمارش ارجاع همیشه بهطور صریح دستکاری میشود. روش معمول این است که از ماکرو Py_INCREF() برای گرفتن ارجاعی جدید به یک شیء (یعنی شمارش ارجاع آن یکی افزایش مییابد) و از Py_DECREF() برای آزاد کردن آن ارجاع (یعنی شمارش ارجاع یکی کاهش مییابد) استفاده شود. ماکرو Py_DECREF() بهمراتب پیچیدهتر از ماکرو incref است، زیرا باید بررسی کند که آیا شمارش ارجاع به صفر میرسد و سپس باعث شود آزادساز حافظهی شیء فراخوانی شود. آزادساز حافظه، اشارهگری به تابع است که در ساختار نوع شیء قرار دارد. آزادساز حافظه مخصوص هر نوع، اگر نوع مربوطه یک نوع شیء مرکب مانند فهرست باشد، آزاد کردن ارجاعهای مربوط به سایر اشیاء موجود در شیء را بر عهده دارد و همچنین هرگونه نهاییسازی اضافی لازم را انجام میدهد. هیچ احتمالی وجود ندارد که شمارش ارجاع سرریز کند؛ برای نگهداشتن شمارش ارجاع، دستکم بهاندازهی تعداد مکانهای متمایز حافظه در حافظهی مجازی بیت استفاده میشود (با فرض sizeof(Py_ssize_t) >= sizeof(void*)). بنابراین، افزایش شمارش ارجاع یک عملیات ساده است.
لازم نیست برای هر متغیر محلی که حاوی اشارهگری به یک شیء است، یک strong reference (یعنی افزایش دادن شمارش ارجاع) نگه دارید. از نظر تئوری، شمارش ارجاع شیء هنگامی که متغیر به آن اشاره میکند، یک واحد افزایش مییابد و هنگامی که متغیر از محدوده خارج میشود، یک واحد کاهش مییابد. اما این دو، اثر یکدیگر را خنثی میکنند، بنابراین در نهایت شمارش ارجاع تغییری نکرده است. تنها دلیل واقعی برای استفاده از شمارش ارجاع، جلوگیری از آزاد شدن شیء در طول مدتی است که متغیر ما به آن اشاره میکند. اگر بدانیم که حداقل یک ارجاع دیگر به شیء وجود دارد که دستکم به اندازهی متغیر ما عمر میکند، نیازی نیست موقتاً یک strong reference جدید (یعنی افزایش دادن شمارش ارجاع) بگیریم. یک موقعیت مهم که این موضوع در آن پیش میآید، هنگامی است که اشیایی بهعنوان آرگومان به توابع C در یک ماژول توسعهای که از پایتون فراخوانی میشوند، پاس داده میشوند؛ سازوکار فراخوانی تضمین میکند که در طول مدت فراخوانی، به هر آرگومان ارجاعی نگه دارد.
با این حال، یک دام رایج این است که شیئی را از یک فهرست استخراج کنید و برای مدتی آن را نگه دارید، بدون اینکه ارجاع جدیدی بگیرید. ممکن است عملیات دیگری شیء را از فهرست حذف کند، آن ارجاع را آزاد کند و شاید حافظهی آن را آزادسازی کند. خطر واقعی این است که عملیاتهای بهظاهر بیضرر ممکن است کد دلخواه پایتون را فراخوانی کنند که بتواند این کار را انجام دهد؛ مسیری در کد وجود دارد که اجازه میدهد کنترل از Py_DECREF() به کاربر بازگردد، بنابراین تقریباً هر عملیاتی بهطور بالقوه خطرناک است.
یک رویکرد امن این است که همیشه از عملیات عام استفاده کنید (توابعی که نامشان با PyObject_، PyNumber_، PySequence_ یا PyMapping_ آغاز میشود). این عملیاتها همیشه یک ارجاع قوی جدید (یعنی شمارش ارجاع را افزایش میدهند) برای شیءای که برمیگردانند ایجاد میکنند. این امر مسئولیت فراخوانی Py_DECREF() را پس از پایان کار با نتیجه بر عهدهی فراخوانکننده میگذارد؛ این کار بهزودی به عادت دوم تبدیل میشود.
جزئیات شمارش ارجاع¶
رفتار شمارش ارجاع توابع در Python/C API به بهترین وجه بر حسب مالکیت ارجاعها توضیح داده میشود. مالکیت به ارجاعها مربوط است، هرگز به اشیاء (اشیاء در مالکیت کسی نیستند: آنها همیشه مشترکاند). «مالک بودن یک ارجاع» به این معناست که مسئول فراخواندن Py_DECREF روی آن هستید وقتی که دیگر به آن ارجاع نیازی نیست. مالکیت همچنین میتواند منتقل شود، به این معنا که کدی که مالکیت ارجاع را دریافت میکند، از آن پس مسئول خواهد بود که سرانجام آن را با فراخوانی Py_DECREF() یا Py_XDECREF() وقتی دیگر مورد نیاز نیست آزاد کند---یا این مسئولیت را (معمولاً به فراخوانکننده خود) واگذار کند. وقتی تابعی مالکیت یک ارجاع را به فراخوانکننده خود منتقل میکند، گفته میشود فراخوانکننده ارجاع جدید دریافت کرده است. وقتی هیچ مالکیتی منتقل نمیشود، گفته میشود فراخوانکننده ارجاع را امانت گرفته است. برای borrowed reference هیچ کاری لازم نیست انجام شود.
برعکس، وقتی یک تابع فراخواننده ارجاعی به یک شیء را ارسال میکند، دو احتمال وجود دارد: تابع ارجاعی به آن شیء را میدزدد، یا نمیدزدد.
دزدیدن ارجاع به این معناست که وقتی ارجاعی را به تابعی پاس میدهید، آن تابع فرض میکند که اکنون مالک آن ارجاع است. از آنجا که مالک جدید میتواند به صلاحدید خود از Py_DECREF() استفاده کند، شما (فراخواننده) نباید پس از فراخوانی از آن ارجاع استفاده کنید.
تعداد کمی از توابع ارجاع میدزدند؛ دو استثنای قابلتوجه PyList_SetItem() و PyTuple_SetItem() هستند که ارجاع به آیتم را میدزدند (اما نه ارجاع به تاپل یا فهرستی که آیتم در آن قرار میگیرد!). این توابع به دلیل یک الگوی رایج برای پر کردن تاپل یا فهرست با اشیاء تازهایجادشده، به گونهای طراحی شدهاند که ارجاعی را بدزدند؛ برای مثال، کد ایجاد تاپل (1, 2, "three") میتواند چیزی شبیه این باشد (فعلاً از مدیریت خطا صرفنظر کنید؛ روش بهتری برای کدنویسی این مورد در ادامه نشان داده شده است):
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("three"));
در اینجا، PyLong_FromLong() ارجاع جدیدی برمیگرداند که بلافاصله توسط PyTuple_SetItem() دزدیده میشود. هرگاه بخواهید با اینکه ارجاع به یک شیء دزدیده خواهد شد، همچنان از آن استفاده کنید، پیش از فراخوانی تابع دزدندهی ارجاع، از Py_INCREF() برای گرفتن ارجاع دیگری استفاده کنید.
ضمناً، PyTuple_SetItem() تنها راه تنظیم آیتمهای تاپل است؛ PySequence_SetItem() و PyObject_SetItem() از انجام این کار امتناع میورزند، زیرا تاپلها نوع دادهای تغییرناپذیر هستند. شما باید PyTuple_SetItem() را تنها برای تاپلهایی که خودتان ایجاد میکنید استفاده کنید.
میتوان کد معادل برای پر کردن یک فهرست را با استفاده از PyList_New() و PyList_SetItem() نوشت.
با این حال، در عمل، شما بهندرت از این روشها برای ایجاد و پر کردن یک تاپل یا فهرست استفاده خواهید کرد. یک تابع عام وجود دارد، Py_BuildValue()، که میتواند با هدایت یک رشته قالب <format string>، بیشتر اشیاء رایج را از مقادیر C ایجاد کند. برای مثال، دو بلوک کد بالا را میتوان با کد زیر جایگزین کرد (که بررسی خطا را نیز انجام میدهد):
PyObject *tuple, *list;
tuple = Py_BuildValue("(iis)", 1, 2, "three");
list = Py_BuildValue("[iis]", 1, 2, "three");
بسیار رایجتر است که از PyObject_SetItem() و توابع مشابه آن با آیتمهایی استفاده کنید که ارجاعهایشان را صرفاً به امانت دارید، مانند آرگومانهایی که به تابعی که در حال نوشتن آن هستید ارسال شدهاند. در چنین حالتی، رفتار این توابع در مورد ارجاعها بسیار منطقیتر است، زیرا لازم نیست صرفاً برای اینکه بتوانید آن ارجاع را واگذار کنید (بگذارید آن «دزدیده» شود)، ارجاع جدیدی بگیرید. برای مثال، این تابع همهی آیتمهای یک فهرست (در واقع، هر دنبالهی تغییرپذیری) را برابر یک آیتم دادهشده قرار میدهد:
int
set_all(PyObject *target, PyObject *item)
{
Py_ssize_t i, n;
n = PyObject_Length(target);
if (n < 0)
return -1;
for (i = 0; i < n; i++) {
PyObject *index = PyLong_FromSsize_t(i);
if (!index)
return -1;
if (PyObject_SetItem(target, index, item) < 0) {
Py_DECREF(index);
return -1;
}
Py_DECREF(index);
}
return 0;
}
وضعیت برای مقادیر بازگشتی توابع کمی متفاوت است. در حالی که گذراندن ارجاع به بیشتر توابع، مسئولیتهای مالکیت شما نسبت به آن ارجاع را تغییر نمیدهد، بسیاری از توابعی که ارجاعی به یک شیء را برمیگردانند، مالکیت آن ارجاع را به شما میدهند. دلیل این امر ساده است: در بسیاری از موارد، شیء بازگرداندهشده در لحظه ایجاد میشود و ارجاعی که دریافت میکنید تنها ارجاع به آن شیء است. بنابراین، توابع عمومیای که ارجاعهای شیء را برمیگردانند، مانند PyObject_GetItem() و PySequence_GetItem()، همیشه یک ارجاع جدید برمیگردانند (فراخوانکننده مالک ارجاع میشود).
مهم است بدانید که اینکه شما مالک ارجاعی هستید که یک تابع برمیگرداند یا نه، تنها به تابعی که فراخوانی میکنید بستگی دارد --- پر و بال (نوع شیئی که بهعنوان آرگومان به تابع داده میشود) در این میان نقشی ندارد! بنابراین، اگر آیتمی را از یک فهرست با استفاده از PyList_GetItem() استخراج کنید، مالک ارجاع نیستید --- اما اگر همان آیتم را از همان فهرست با استفاده از PySequence_GetItem() (که اتفاقاً دقیقاً همان آرگومانها را میگیرد) به دست آورید، مالک ارجاعی به شیء برگرداندهشده هستید.
در اینجا مثالی از نحوهی نوشتن تابعی که مجموع آیتمهای فهرستی از اعداد صحیح را محاسبه میکند آورده شده است؛ یک بار با استفاده از PyList_GetItem() و یک بار با استفاده از PySequence_GetItem().
long
sum_list(PyObject *list)
{
Py_ssize_t i, n;
long total = 0, value;
PyObject *item;
n = PyList_Size(list);
if (n < 0)
return -1; /* Not a list */
for (i = 0; i < n; i++) {
item = PyList_GetItem(list, i); /* Can't fail */
if (!PyLong_Check(item)) continue; /* Skip non-integers */
value = PyLong_AsLong(item);
if (value == -1 && PyErr_Occurred())
/* Integer too big to fit in a C long, bail out */
return -1;
total += value;
}
return total;
}
long
sum_sequence(PyObject *sequence)
{
Py_ssize_t i, n;
long total = 0, value;
PyObject *item;
n = PySequence_Length(sequence);
if (n < 0)
return -1; /* Has no length */
for (i = 0; i < n; i++) {
item = PySequence_GetItem(sequence, i);
if (item == NULL)
return -1; /* Not a sequence, or other failure */
if (PyLong_Check(item)) {
value = PyLong_AsLong(item);
Py_DECREF(item);
if (value == -1 && PyErr_Occurred())
/* Integer too big to fit in a C long, bail out */
return -1;
total += value;
}
else {
Py_DECREF(item); /* Discard reference ownership */
}
}
return total;
}
نوعها¶
چند نوع دادهی دیگر نیز نقش مهمی در Python/C API ایفا میکنند؛ بیشتر آنها نوعهای سادهی C مانند int، long، double و char* هستند. چند نوع ساختاری برای توصیف جدولهای ایستایی که توابع اکسپورتشده توسط یک ماژول یا ویژگیهای دادهی یک نوع شیء جدید را فهرست میکنند، به کار میروند، و نوع دیگری برای توصیف مقدار یک عدد مختلط استفاده میشود. این موارد همراه با توابعی که از آنها استفاده میکنند بحث خواهند شد.
-
type Py_ssize_t¶
- قسمتی از ABI پایدار.
یک نوع صحیح علامتدار بهگونهای که
sizeof(Py_ssize_t) == sizeof(size_t)باشد. استاندارد C99 چنین چیزی را مستقیماً تعریف نمیکند (size_t یک نوع صحیح بدون علامت است). برای جزئیات به PEP 353 مراجعه کنید.PY_SSIZE_T_MAXبزرگترین مقدار مثبت از نوعPy_ssize_tاست.
استثناها¶
برنامهنویس پایتون تنها در صورتی نیاز به رسیدگی به استثناها دارد که مدیریت خطای خاصی لازم باشد؛ استثناهای مدیریتنشده بهطور خودکار به فراخواننده منتشر میشوند، سپس به فراخوانندهی فراخواننده، و به همین ترتیب، تا اینکه به مفسر سطح بالا برسند، جایی که همراه با یک ردگیری پشته به کاربر گزارش میشوند.
اما برای برنامهنویسان C، بررسی خطا همیشه باید صریح باشد. همهی توابع در Python/C API میتوانند استثنا ایجاد کنند، مگر آنکه در مستندات یک تابع، خلاف آن بهطور صریح ادعا شده باشد. بهطور کلی، وقتی تابعی با خطا مواجه میشود، یک استثنا را تنظیم میکند، ارجاعهای شیءای را که مالک آنهاست دور میریزد و یک نشانگر خطا برمیگرداند. اگر خلاف آن مستند نشده باشد، این نشانگر یا NULL است یا -1، بسته به نوع بازگشتی تابع. چند تابع نتیجهی بولی درست/غلط برمیگردانند که در آنها مقدار غلط نشاندهندهی خطا است. تعداد بسیار کمی از توابع هیچ نشانگر خطای صریحی برنمیگردانند یا مقدار بازگشتی مبهمی دارند و نیازمند آزمون صریح خطاها با PyErr_Occurred() هستند. این استثناها همیشه بهطور صریح مستند میشوند.
وضعیت استثنا در ذخیرهگاه اختصاصی هر نخ نگهداری میشود (این معادل استفاده از ذخیرهگاه سراسری در یک برنامهی بدون نخ است). یک نخ میتواند در یکی از دو وضعیت باشد: استثنایی رخ داده است یا نه. میتوان از تابع PyErr_Occurred() برای بررسی این موضوع استفاده کرد: این تابع هنگامی که استثنایی رخ داده باشد، یک ارجاع امانتی به شیء نوع استثنا برمیگرداند و در غیر این صورت NULL را برمیگرداند. توابع متعددی برای تنظیم وضعیت استثنا وجود دارند: PyErr_SetString() رایجترین (هرچند نه عامترین) تابع برای تنظیم وضعیت استثنا است و PyErr_Clear() وضعیت استثنا را پاک میکند.
وضعیت کامل استثنا از سه شیء تشکیل شده است (که هر سه میتوانند NULL باشند): نوع استثنا، مقدار متناظر استثنا، و ردگیری. اینها همان معانی نتیجهی پایتونی sys.exc_info() را دارند؛ با این حال، یکسان نیستند: اشیاء پایتونی آخرین استثنایی را نشان میدهند که توسط یک دستور try ... except پایتونی در حال مدیریت است، در حالی که وضعیت استثنا در سطح C تنها زمانی وجود دارد که استثنایی بین توابع C در حال انتقال باشد تا اینکه به حلقهی اصلی مفسر بایتکد پایتون برسد؛ حلقهای که کار انتقال آن به sys.exc_info() و توابع مشابه را بر عهده دارد.
توجه داشته باشید که از پایتون 1.5 به بعد، روش ترجیحی و نخایمن برای دسترسی به وضعیت استثنا از درون کد پایتون، فراخوانی تابع sys.exc_info() است که وضعیت استثنای هر نخ را برای کد پایتون برمیگرداند. همچنین، معناشناسی هر دو روش دسترسی به وضعیت استثنا تغییر کرده است، بهطوری که تابعی که استثنایی را میگیرد، وضعیت استثنای نخ خود را ذخیره و بازیابی میکند تا وضعیت استثنای فراخوانندهاش حفظ شود. این امر از اشکالهای رایج در کد مدیریت استثنا که ناشی از بازنویسی استثنای در حال مدیریت توسط تابعی بهظاهر بیآزار هستند جلوگیری میکند؛ همچنین تمدیدِ اغلب ناخواستهی طول عمر اشیایی را که فریمهای پشته در ردگیری به آنها ارجاع میدهند، کاهش میدهد.
بهعنوان یک اصل کلی، تابعی که برای انجام کاری تابع دیگری را فراخوانی میکند، باید بررسی کند که آیا تابع فراخوانیشده استثنایی ایجاد کرده است یا خیر، و در این صورت، وضعیت استثنا را به فراخواننده خود منتقل کند. این تابع باید هر ارجاع شیءای را که مالک آن است دور بریزد و یک نشانگر خطا برگرداند، اما نباید استثنای دیگری تنظیم کند --- زیرا این کار باعث میشد که استثنایی که بهتازگی ایجاد شده بازنویسی شود و اطلاعات مهمی دربارهی علت دقیق خطا از دست برود.
یک مثال ساده از تشخیص استثناها و عبور دادن آنها در مثال sum_sequence() بالا نشان دادهشده است. اتفاقاً این مثال هنگام تشخیص خطا نیازی به پاکسازی هیچیک از ارجاعهای مالکیتشده ندارد. تابع مثال زیر مقداری پاکسازی خطا را نشان میدهد. ابتدا، برای یادآوری اینکه چرا پایتون را دوست دارید، کد معادل پایتون را نشان میدهیم:
def incr_item(dict, key):
try:
item = dict[key]
except KeyError:
item = 0
dict[key] = item + 1
در اینجا کد C مربوطه، در تمام شکوهش، آمده است:
int
incr_item(PyObject *dict, PyObject *key)
{
/* Objects all initialized to NULL for Py_XDECREF */
PyObject *item = NULL, *const_one = NULL, *incremented_item = NULL;
int rv = -1; /* Return value initialized to -1 (failure) */
item = PyObject_GetItem(dict, key);
if (item == NULL) {
/* Handle KeyError only: */
if (!PyErr_ExceptionMatches(PyExc_KeyError))
goto error;
/* Clear the error and use zero: */
PyErr_Clear();
item = PyLong_FromLong(0L);
if (item == NULL)
goto error;
}
const_one = PyLong_FromLong(1L);
if (const_one == NULL)
goto error;
incremented_item = PyNumber_Add(item, const_one);
if (incremented_item == NULL)
goto error;
if (PyObject_SetItem(dict, key, incremented_item) < 0)
goto error;
rv = 0; /* Success */
/* Continue with cleanup code */
error:
/* Cleanup code, shared by success and failure path */
/* Use Py_XDECREF() to ignore NULL references */
Py_XDECREF(item);
Py_XDECREF(const_one);
Py_XDECREF(incremented_item);
return rv; /* -1 for error, 0 for success */
}
این مثال، کاربردی مورد تأیید از دستور goto در C را به نمایش میگذارد! این مثال، استفاده از PyErr_ExceptionMatches() و PyErr_Clear() برای مدیریت استثناهای خاص و استفاده از Py_XDECREF() برای آزاد کردن ارجاعهای مالکیتدار (owned) که ممکن است NULL باشند را نشان میدهد (به 'X' در نام توجه کنید؛ Py_DECREF() هنگام مواجهه با ارجاع NULL فروپاشی میکند). برای اینکه این کار کند، مهم است که متغیرهایی که برای نگهداشتن ارجاعهای مالکیتدار به کار میروند، به NULL مقداردهی اولیه شده باشند؛ به همین ترتیب، مقدار بازگشتی پیشنهادی نیز به -1 (شکست) مقداردهی اولیه میشود و تنها پس از آنکه آخرین فراخوانی انجامشده موفق باشد، روی موفقیت تنظیم میشود.
تعبیه پایتون¶
تنها کار مهمی که فقط تعبیهکنندگان (embedders) مفسر پایتون — برخلاف نویسندگان ماژولهای توسعهای — باید نگران آن باشند، مقداردهی اولیه و احتمالاً نهاییسازی مفسر پایتون است. بیشترِ کارکردهای مفسر تنها پس از آنکه مفسر مقداردهی اولیه شده باشد، قابل استفادهاند.
تابع مقداردهی اولیهی پایه Py_Initialize() است. این تابع جدول ماژولهای بارگذاریشده را مقداردهی اولیه میکند و ماژولهای بنیادی builtins، __main__ و sys را ایجاد میکند. همچنین مسیر جستجوی ماژول (sys.path) را مقداردهی اولیه میکند.
Py_Initialize() فهرست آرگومانهای اسکریپت (sys.argv) را تنظیم نمیکند. اگر این متغیر توسط کد پایتونی که بعداً اجرا خواهد شد مورد نیاز باشد، باید PyConfig.argv و PyConfig.parse_argv تنظیم شوند: به پیکربندی مقداردهی اولیه پایتون مراجعه کنید.
در بیشتر سیستمها (بهطور خاص در یونیکس و ویندوز، هرچند جزئیات اندکی متفاوت است)، Py_Initialize() مسیر جستجوی ماژول را بر اساس بهترین حدس خود برای مکان پروندهی اجرایی مفسر استاندارد پایتون محاسبه میکند، با این فرض که کتابخانهی پایتون در مکانی ثابت نسبت به پروندهی اجرایی مفسر پایتون قرار دارد. بهطور خاص، این تابع به دنبال پوشهای با نام lib/pythonX.Y نسبت به پوشهی والدِ مکانی میگردد که پروندهی اجرایی با نام python در مسیر جستجوی فرمان پوسته (متغیر محیط PATH) در آن یافت میشود.
برای نمونه، اگر پرونده اجرایی پایتون در /usr/local/bin/python یافت شود، فرض میکند که کتابخانهها در /usr/local/lib/pythonX.Y قرار دارند. (در واقع، این مسیر خاص همچنین مکان «جایگزین» است که زمانی استفاده میشود که هیچ پرونده اجراییای با نام python در PATH یافت نشود.) کاربر میتواند این رفتار را با تنظیم متغیر محیطی PYTHONHOME بازتعریف کند، یا با تنظیم PYTHONPATH پوشههای اضافی را در ابتدای مسیر استاندارد درج کند.
برنامه جاسازنده میتواند با تنظیم PyConfig.program_name پیش از فراخوانی Py_InitializeFromConfig() جستجو را هدایت کند. توجه داشته باشید که PYTHONHOME همچنان این تنظیم را لغو میکند و PYTHONPATH همچنان در ابتدای مسیر استاندارد درج میشود. برنامهای که به کنترل کامل نیاز دارد، باید پیادهسازی خودش از Py_GetPath()، Py_GetPrefix()، Py_GetExecPrefix() و Py_GetProgramFullPath() را فراهم کند (که همگی در Modules/getpath.c تعریف شدهاند).
گاهی مطلوب است که پایتون را «مقداردهیزدایی» (uninitialize) کنید. برای نمونه، ممکن است برنامه بخواهد از نو شروع کند (فراخوانی دوبارهی Py_Initialize()) یا اینکه کار برنامه با پایتون بهسادگی تمام شده باشد و بخواهد حافظهای را که پایتون تخصیص داده است آزاد کند. این کار را میتوان با فراخوانی Py_FinalizeEx() انجام داد. تابع Py_IsInitialized() در صورتی که پایتون در حال حاضر در وضعیت مقداردهیشده باشد، مقدار true برمیگرداند. اطلاعات بیشتر دربارهی این توابع در فصلی بعدی ارائه شده است. توجه داشته باشید که Py_FinalizeEx() تمام حافظهی تخصیصیافته توسط مفسر پایتون را آزاد نمیکند؛ مثلاً حافظهی تخصیصیافته توسط ماژولهای توسعهای در حال حاضر قابل آزادسازی نیست.
ساختهای اشکالزدایی¶
پایتون را میتوان با چندین ماکرو ساخت تا بررسیهای اضافی مفسر و ماژولهای توسعهای فعال شوند. این بررسیها معمولاً سربار زیادی به رانتایم اضافه میکنند، بنابراین بهطور پیشفرض فعال نیستند.
فهرست کامل انواع مختلف ساختهای اشکالزدایی در پرونده Misc/SpecialBuilds.txt در توزیع کد منبع پایتون قرار دارد. ساختهایی در دسترس هستند که از ردگیری شمارش ارجاع، اشکالزدایی تخصیصدهنده حافظه یا پروفایلگیری سطح پایین حلقه اصلی مفسر پشتیبانی میکنند. در ادامه این بخش، تنها پرکاربردترین ساختها توضیح داده خواهند شد.
-
Py_DEBUG¶
کامپایل کردن مفسر در حالی که ماکروی Py_DEBUG تعریفشده باشد، چیزی را تولید میکند که عموماً از ساخت دیباگ پایتون منظور میشود. ماکروی Py_DEBUG در ساخت یونیکس با افزودن --with-pydebug به دستور ./configure فعال میشود. این ماکرو همچنین با وجود ماکروی _DEBUG که مختص پایتون نیست، بهطور ضمنی فعال میشود. هنگامی که Py_DEBUG در ساخت یونیکس فعال باشد، بهینهسازی کامپایلر غیرفعال میشود.
علاوه بر اشکالزدایی شمارش ارجاع که در زیر شرح داده شده، بررسیهای اضافی نیز انجام میشود؛ به نسخه دیباگ پایتون مراجعه کنید.
تعریف Py_TRACE_REFS ردگیری ارجاع را فعال میکند (به configure --with-trace-refs option مراجعه کنید). هنگامی که تعریف شود، با افزودن دو فیلد اضافی به هر PyObject، یک فهرست پیوندی دوطرفهی چرخهای از اشیاء فعال نگهداری میشود. مجموع تخصیصها نیز پیگیری میشود. هنگام خروج، تمام ارجاعهای موجود چاپ میشوند. (در حالت تعاملی، این کار پس از اجرای هر دستور توسط مفسر انجام میشود.)
لطفاً برای اطلاعات مفصلتر به Misc/SpecialBuilds.txt در توزیع منبع پایتون مراجعه کنید.
ابزارهای شخص ثالث پیشنهادی¶
ابزارهای شخص ثالث زیر رویکردهایی هم سادهتر و هم پیشرفتهتر برای ایجاد افزونههای C، C++ و Rust برای پایتون ارائه میدهند:
استفاده از چنین ابزارهایی میتواند به شما کمک کند تا از نوشتن کدی که بهشدت به نسخهی خاصی از سیپایتون وابسته است، اجتناب کنید، از خطاهای شمارش ارجاع بپرهیزید و بیشتر بر کد خودتان تمرکز کنید تا بر استفاده از API سیپایتون. بهطور کلی، میتوان با بهروزرسانی ابزار، از نسخههای جدید پایتون پشتیبانی کرد و کد شما اغلب بهطور خودکار از APIهای جدیدتر و کارآمدتر استفاده خواهد کرد. برخی از ابزارها همچنین از کامپایل برای پیادهسازیهای دیگر پایتون با یک مجموعه واحد از کدهای منبع پشتیبانی میکنند.
این پروژهها توسط همان افرادی که پایتون را نگهداری میکنند، پشتیبانی نمیشوند و مشکلات باید مستقیماً با خود پروژهها مطرح شوند. به یاد داشته باشید که بررسی کنید پروژه هنوز نگهداری و پشتیبانی میشود، زیرا ممکن است فهرست بالا از تاریخ بیفتد.
همچنین ملاحظه نمائید
- راهنمای کاربری بستهبندی پایتون: افزونههای دودویی
راهنمای کاربر بستهبندی پایتون (Python Packaging User Guide) نهتنها چندین ابزار موجود را پوشش میدهد که ایجاد توسعههای دودویی را ساده میکنند، بلکه به دلایل مختلفی نیز میپردازد که چرا از اساس ایجاد یک ماژول توسعهای میتواند مطلوب باشد.