GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing python.github.io — استفاده از C API: موضوعات متفرقه استفاده از C API: موضوعات متفرقه¶آموزش شما را در ایجاد یک ماژول توسعهی C API راهنمایی کرد، اما بسیاری از قسمتها را بدون توضیح گذاشت. این سند چندین مفهوم را بررسی میکند که برای نوشتن مشتقات پیچیدهتر باید آنها را یاد بگیرید. خطاها و استثناها¶یک قرارداد مهم در سراسر مفسر پایتون به شرح زیر است: هنگامی که تابعی شکست میخورد، باید وضعیت استثنا را تنظیم کند و یک مقدار خطا (معمولاً API پایتون تعدادی تابع برای تنظیم انواع مختلف استثناها تعریف میکند. رایجترین آنها تابع مفید دیگر میتوانید بهصورت غیرمخرب با وقتی تابعی f که تابع دیگری به نام g را فراخوانی میکند تشخیص میدهد که آن تابع شکست خورده است، f باید خودش یک مقدار خطا برگرداند (معمولاً (گاهی موقعیتهایی پیش میآید که یک ماژول میتواند در واقع با فراخوانی تابع دیگری از نوع برای نادیدهگرفتن استثنایی که توسط یک فراخوانی تابع ناموفق تنظیم شده است، باید وضعیت استثنا بهطور صریح با فراخوانی هر فراخوانی ناموفق همچنین توجه داشته باشید که، با استثنای مهم در نهایت، هنگامی که نشانگر خطا را برمیگردانید، مراقب باشید که زبالهها را پاکسازی کنید (با انجام فراخوانیهای انتخاب اینکه کدام استثنا را برافرازید، کاملاً با شماست. اشیاء C از پیش اعلانشدهای متناظر با تمام استثناهای توکار پایتون وجود دارند، مانند شما همچنین میتوانید یک استثنای جدید تعریف کنید که مختص ماژول شما باشد. سادهترین راه برای انجام این کار، اعلان یک متغیر شیء سراسری ایستا در ابتدای پرونده است: static PyObject *SpamError = NULL; و آن را با فراخوانی SpamError = PyErr_NewException("spam.error", NULL, NULL); از آنجا که فعلاً، اجازه دهید از این مسئله اجتناب کنیم: راهاندازی مکرر را با پرتاب static PyObject *SpamError = NULL; static int spam_module_exec(PyObject *m) { if (SpamError != NULL) { PyErr_SetString(PyExc_ImportError, "cannot initialize spam module more than once"); return -1; } SpamError = PyErr_NewException("spam.error", NULL, NULL); if (PyModule_AddObjectRef(m, "SpamError", SpamError) < 0) { return -1; } return 0; } static PyModuleDef_Slot spam_module_slots[] = { {Py_mod_exec, spam_module_exec}, {0, NULL} }; static struct PyModuleDef spam_module = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "spam", .m_size = 0, // non-negative .m_slots = spam_module_slots, }; PyMODINIT_FUNC PyInit_spam(void) { return PyModuleDef_Init(&spam_module); } توجه داشته باشید که نام پایتونی شیء استثنا همچنین توجه داشته باشید که متغیر در حال حاضر، فراخوانی استفاده از میتوانید استثنای static PyObject * spam_system(PyObject *self, PyObject *args) { const char *command; int sts; if (!PyArg_ParseTuple(args, "s", &command)) return NULL; sts = system(command); if (sts < 0) { PyErr_SetString(SpamError, "System command failed"); return NULL; } return PyLong_FromLong(sts); } تعبیهی یک افزونه¶اگر میخواهید ماژول خود را به بخش دائمی مفسر پایتون تبدیل کنید، باید تنظیمات پیکربندی را تغییر دهید و مفسر را دوباره بسازید. در سیستمیونیکس، پروندهی خود (مثلاً spam spammodule.o
و مفسر را با اجرای make در پوشهی سطح بالا بازسازی کنید. همچنین میتوانید make را در زیرپوشهی اگر ماژول شما به کتابخانههای اضافی برای پیوند دادن نیاز دارد، میتوانید آنها را نیز در همان سطر در پرونده پیکربندی فهرست کنید، برای مثال: spam spammodule.o -lX11 فراخوانی توابع پایتون از زبان C¶آموزش بر روی فراخوانیپذیر کردن عملکردهای C از پایتون تمرکز داشت. حالت برعکس آن نیز مفید است: فراخوانی توابع پایتون از C. این موضوع بهویژه برای کتابخانههایی که از توابع موسوم به «کالبک» پشتیبانی میکنند اهمیت دارد. اگر یک رابط C از کالبکها استفاده کند، نسخهی معادل در پایتون اغلب باید یک مکانیزم کالبک را برای برنامهنویس پایتون فراهم کند؛ پیادهسازی آن نیازمند فراخوانی توابع کالبک پایتون از طریق یک کالبک C خواهد بود. کاربردهای دیگری نیز قابل تصور است. خوشبختانه، مفسر پایتون بهراحتی بهصورت بازگشتی فراخوانی میشود و یک رابط استاندارد برای فراخوانی یک تابع پایتون وجود دارد. (اگر میخواهید بدانید چگونه میتوان پارسر پایتون را با یک رشته خاص بهعنوان ورودی فراخوانی کرد، به لایه سطح بسیار بالا مراجعه کنید.) فراخوانی یک تابع پایتون آسان است. نخست، برنامه پایتون باید به نحوی شیء تابع پایتون را به شما پاس دهد. شما باید تابعی (یا رابط دیگری) برای انجام این کار فراهم کنید. وقتی این تابع فراخوانی شد، اشارهگری به شیء تابع پایتون را (مراقب باشید که آن را static PyObject *my_callback = NULL; static PyObject * my_set_callback(PyObject *dummy, PyObject *args) { PyObject *result = NULL; PyObject *temp; if (PyArg_ParseTuple(args, "O:set_callback", &temp)) { if (!PyCallable_Check(temp)) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } Py_XINCREF(temp); /* Add a reference to new callback */ Py_XDECREF(my_callback); /* Dispose of previous callback */ my_callback = temp; /* Remember new callback */ /* Boilerplate to return "None" */ Py_INCREF(Py_None); result = Py_None; } return result; } این تابع باید با استفاده از پرچم ماکروهای بعداً، وقتی زمان فراخوانی تابع فرا میرسد، شما تابع Cِ int arg; PyObject *arglist; PyObject *result; ... arg = 123; ... /* Time to call the callback */ arglist = Py_BuildValue("(i)", arg); result = PyObject_CallObject(my_callback, arglist); Py_DECREF(arglist);
مقدار بازگشتی با این حال، پیش از انجام این کار، مهم است که بررسی کنید مقدار بازگشتی if (result == NULL) return NULL; /* Pass error back */ ...use result... Py_DECREF(result); بسته به رابط مورد نظر برای تابع کالبک پایتون، ممکن است لازم باشد فهرست آرگومانها را نیز به PyObject *arglist; ... arglist = Py_BuildValue("(l)", eventcode); result = PyObject_CallObject(my_callback, arglist); Py_DECREF(arglist); if (result == NULL) return NULL; /* بازگرداندن خطا */ /* اینجا شاید از نتیجه استفاده شود */ Py_DECREF(result); Note the placement of شما همچنین میتوانید با استفاده از PyObject *dict; ... dict = Py_BuildValue("{s:i}", "name", val); result = PyObject_Call(my_callback, NULL, dict); Py_DECREF(dict); if (result == NULL) return NULL; /* بازگرداندن خطا */ /* اینجا شاید از نتیجه استفاده کنید */ Py_DECREF(result); استخراج پارامترها در توابع توسعهای¶آموزش از یک تابع " برای بازکردن بستهی تاپل، سیپایتون تابع int PyArg_ParseTuple(PyObject *arg, const char *format, ...); آرگومان arg باید یک شیء تاپل حاوی فهرست آرگومانی باشد که از پایتون به یک تابع C پاس داده میشود. آرگومان format باید یک رشته قالب باشد که سینتکس آن در تجزیه آرگومانها و ساخت مقادیر در راهنمای مرجع Python/C API توضیح داده شده است. آرگومانهای باقیمانده باید آدرسهای متغیرهایی باشند که نوع آنها توسط رشته قالب تعیین میشود. به عنوان مثال، برای دریافت یک شیء const char *command; if (!PyArg_ParseTuple(args, "s", &command)) { return NULL; } اگر خطایی در فهرست آرگومانها تشخیص داده شود، تابع توجه داشته باشید که هرچند توجه داشته باشید که هر ارجاع به شیء پایتون که در اختیار فراخواننده قرار میگیرد، یک ارجاع امانتی است؛ شمارش ارجاع آنها را کاهش ندهید! چند نمونه فراخوانی: #include <Python.h> int ok; int i, j; long k, l; const char *s; Py_ssize_t size; ok = PyArg_ParseTuple(args, ""); /* No arguments */ /* Python call: f() */ ok = PyArg_ParseTuple(args, "s", &s); /* A string */ /* Possible Python call: f('whoops!') */ ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* دو long و یک رشته */ /* فراخوانی ممکن پایتون: f(1, 2, 'three') */ ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size); /* یک جفت عدد صحیح و یک رشته، که اندازهی آن نیز برگردانده میشود */ /* فراخوانی ممکن پایتون: f((1, 2), 'three') */ { const char *file; const char *mode = "r"; int bufsize = 0; ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize); /* A string, and optionally another string and an integer */ /* Possible Python calls: f('spam') f('spam', 'w') f('spam', 'wb', 100000) */ } { int left, top, right, bottom, h, v; ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)", &left, &top, &right, &bottom, &h, &v); /* A rectangle and a point */ /* Possible Python call: f(((0, 0), (400, 300)), (10, 10)) */ } { Py_complex c; ok = PyArg_ParseTuple(args, "D:myfunction", &c); /* a complex, also providing a function name for errors */ /* Possible Python call: myfunction(1+2j) */ } پارامترهای کلیدواژهای برای توابع توسعهای¶اگر میخواهید تابع شما آرگومانهای کلیدواژهای را نیز بپذیرد، از پرچم در این صورت، تابع C باید یک پارامتر سوم تابع int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict, const char *format, char * const *kwlist, ...); پارامترهای arg و format با پارامترهای تابع نکته تاپلهای تودرتو هنگام استفاده از آرگومانهای کلیدواژهای قابل تجزیه نیستند! پارامترهای کلیدواژهای که پاس داده میشوند و در kwlist وجود ندارند، باعث بهوجود آمدن در اینجا یک ماژول نمونه که از کلیدواژهها استفاده میکند، بر اساس نمونهای از Geoff Philbrick (philbrick@hks.com) آمده است: #define PY_SSIZE_T_CLEAN #include <Python.h> static PyObject * keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds) { int voltage; const char *state = "a stiff"; const char *action = "voom"; const char *type = "Norwegian Blue"; static char *kwlist[] = {"voltage", "state", "action", "type", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist, &voltage, &state, &action, &type)) return NULL; printf("-- This parrot wouldn't %s if you put %i Volts through it.\n", action, voltage); printf("-- Lovely plumage, the %s -- It's %s!\n", type, state); Py_RETURN_NONE; } static PyMethodDef keywdarg_methods[] = { /* The cast of the function is necessary since PyCFunction values * only take two PyObject* parameters, and keywdarg_parrot() takes * three. */ {"parrot", (PyCFunction)(void(*)(void))keywdarg_parrot, METH_VARARGS | METH_KEYWORDS, "Print a lovely skit to standard output."}, {NULL, NULL, 0, NULL} /* sentinel */ }; ساخت مقادیر دلخواه¶این تابع همتای PyObject *Py_BuildValue(const char *format, ...); این تابع مجموعهای از واحدهای قالب (format units) را تشخیص میدهد که مشابه واحدهای شناساییشده توسط یک تفاوت با مثالها (در سمت چپ فراخوانی، در سمت راست مقدار حاصل در پایتون): Py_BuildValue("") None
Py_BuildValue("i", 123) 123
Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)
Py_BuildValue("s", "hello") 'hello'
Py_BuildValue("y", "hello") b'hello'
Py_BuildValue("ss", "hello", "world") ('hello', 'world')
Py_BuildValue("s#", "hello", 4) 'hell'
Py_BuildValue("y#", "hello", 4) b'hell'
Py_BuildValue("()") ()
Py_BuildValue("(i)", 123) (123,)
Py_BuildValue("(ii)", 123, 456) (123, 456)
Py_BuildValue("(i,i)", 123, 456) (123, 456)
Py_BuildValue("[i,i]", 123, 456) [123, 456]
Py_BuildValue("{s:i,s:i}",
"abc", 123, "def", 456) {'abc': 123, 'def': 456}
Py_BuildValue("((ii)(ii)) (ii)",
1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
شمارش ارجاع¶در زبانهایی مانند C یا C++، برنامهنویس مسئول تخصیص پویا و آزادسازی حافظه روی هیپ است. در C، این کار با استفاده از توابع هر بلوک حافظهای که با علتهای رایج نشت حافظه، مسیرهای غیرمعمول در کد هستند. برای مثال، ممکن است یک تابع بلوکی از حافظه را تخصیص دهد، محاسبهای انجام دهد و سپس بلوک را دوباره آزاد کند. اکنون تغییری در نیازمندیهای تابع ممکن است آزمونی به محاسبه اضافه کند که شرط خطا را تشخیص میدهد و میتواند زودهنگام از تابع بازگردد. آسان است که آزاد کردن بلوک حافظهی تخصیصیافته هنگام این خروج زودهنگام فراموش شود، بهخصوص وقتی که این خروج بعداً به کد اضافه میشود. چنین نشتهایی، پس از ایجاد، اغلب مدت طولانی تشخیص داده نمیشوند: خروج خطا فقط در بخش کوچکی از همهی فراخوانیها رخ میدهد و بیشتر ماشینهای مدرن حافظهی مجازی فراوانی دارند، بنابراین نشت تنها در فرایند طولانیمدتی که مکرراً از تابع نشتکننده استفاده میکند آشکار میشود. از این رو، مهم است که با داشتن قرارداد یا راهبرد کدنویسیای که این نوع خطاها را به حداقل میرساند، از وقوع نشتها جلوگیری کنید. از آنجا که پایتون بهطور گسترده از راهبردی جایگزین، زبالهروبی خودکار (automatic garbage collection) نامیده میشود. (گاهی شمارش ارجاع نیز بهعنوان یک راهبرد زبالهروبی شناخته میشود، از این رو از واژهی «خودکار» برای تمایز میان این دو استفاده میشود.) مزیت بزرگ زبالهروبی خودکار این است که کاربر نیازی به فراخوانی صریح در حالی که پایتون از پیادهسازی سنتی شمارش ارجاع استفاده میکند، همچنین آشکارساز چرخهای ارائه میدهد که برای تشخیص چرخههای ارجاع کار میکند. این امر به برنامهها اجازه میدهد که نگران ایجاد ارجاعهای دایرهای مستقیم یا غیرمستقیم نباشند؛ این ارجاعها نقطهضعف زبالهروبیای هستند که تنها با استفاده از شمارش ارجاع پیادهسازی شده است. چرخههای ارجاع از اشیایی تشکیل شدهاند که ارجاعهایی (احتمالاً غیرمستقیم) به خود دارند، بهطوری که هر شیء در چرخه تعداد ارجاعی غیر از صفر دارد. پیادهسازیهای متداول شمارش ارجاع نمیتوانند حافظهی متعلق به هیچیک از اشیای درون یک چرخه ارجاع، یا اشیایی که از سوی اشیای درون چرخه به آنها ارجاع شده است، را بازیابی کنند، هرچند هیچ ارجاع دیگری به خود چرخه وجود ندارد. آشکارساز چرخه میتواند چرخههای زباله را تشخیص دهد و آنها را بازپسگیری کند. ماژول شمارش ارجاع در پایتون¶دو ماکرو وجود دارد، اکنون پرسش بزرگ این است: چه زمانی باید از همچنین میتوان یک ارجاع به یک شیء را بهصورت امانتی [1] دریافت کرد. امانتگیرندهی یک ارجاع نباید مزیت امانت گرفتن ارجاع نسبت به مالکیت آن این است که لازم نیست در تمام مسیرهای ممکن در کد، دفع ارجاع را مدیریت کنید --- به عبارت دیگر، با ارجاع امانتی، هنگام خروج زودهنگام در معرض خطر نشت قرار نمیگیرید. عیب امانت گرفتن نسبت به مالکیت این است که در برخی موقعیتهای ظریف، ممکن است در کدی که بهظاهر درست است، از یک ارجاع امانتی پس از آنکه مالکی که ارجاع از او امانت گرفته شده، در واقع آن را دفع کرده باشد، استفاده شود. یک ارجاع امانتی را میتوان با فراخوانی قواعد مالکیت¶هرگاه ارجاعی به یک شیء به داخل یا خارج از یک تابع پاس داده شود، اینکه آیا مالکیت به همراه ارجاع منتقل میشود یا خیر، بخشی از مشخصات رابط تابع است. بیشتر توابعی که ارجاعی به یک شیء را برمیگردانند، مالکیت را همراه با ارجاع منتقل میکنند. بهطور خاص، تمام توابعی که وظیفهشان ایجاد یک شیء جدید است، مانند بسیاری از توابعی که اشیاء را از اشیاء دیگر استخراج میکنند، مالکیت را نیز همراه با ارجاع منتقل میکنند؛ برای مثال تابع وقتی یک ارجاع به شیء را به تابع دیگری میدهید، بهطور کلی، تابع ارجاع را بهصورت امانتی از شما میگیرد --- اگر نیاز به ذخیرهسازی آن داشته باشد، از وقتی یک تابع C از پایتون فراخوانی میشود، ارجاعهایی به آرگومانهای خود را از فراخوانیکننده به امانت میگیرد. فراخوانیکننده مالک ارجاعی به شیء است، بنابراین طول عمر ارجاع امانتی تا زمان بازگشت تابع تضمینشده است. تنها زمانی که چنین ارجاع امانتیای باید ذخیره یا منتقل شود، باید با فراخوانی ارجاع شیءای که از یک تابع C فراخوانیشده از پایتون بازگردانده میشود، باید یک ارجاع مالکانه (owned reference) باشد --- مالکیت از تابع به فراخوانندهی آن منتقل میشود. یخ نازک¶چند موقعیت وجود دارد که استفادهی بهظاهر بیضرر از یک ارجاع امانتی میتواند منجر به مشکلات شود. همهی این موارد به فراخوانیهای ضمنی مفسر مربوطاند که میتوانند باعث شوند مالک یک ارجاع آن را رها کند. نخستین و مهمترین موردی که باید از آن آگاه باشید، استفاده از void bug(PyObject *list) { PyObject *item = PyList_GetItem(list, 0); PyList_SetItem(list, 1, PyLong_FromLong(0L)); PyObject_Print(item, stdout, 0); /* BUG! */ } این تابع ابتدا یک ارجاع امانتی به بیایید جریان کنترل را تا داخل از آنجا که به زبان پایتون نوشته شده است، متد راهحل، وقتی منشأ مشکل را بدانید، آسان است: شمارش ارجاع را بهطور موقت افزایش دهید. نسخه صحیح تابع چنین است: void no_bug(PyObject *list) { PyObject *item = PyList_GetItem(list, 0); Py_INCREF(item); PyList_SetItem(list, 1, PyLong_FromLong(0L)); PyObject_Print(item, stdout, 0); Py_DECREF(item); } این داستانی واقعی است. نسخهای قدیمیتر از پایتون شامل گونههایی از این باگ بود و کسی مدت زمان قابل توجهی را در دیباگر C صرف کرد تا بفهمد چرا متدهای دومین مورد از مشکلات مربوط به ارجاع امانتی، گونهای است که به نخها مربوط میشود. بهطور معمول، نخهای متعدد در مفسر پایتون نمیتوانند مزاحم کار یکدیگر شوند، زیرا یک قفل سراسری از کل فضای اشیای پایتون محافظت میکند. با این حال، میتوان این قفل را بهطور موقت با استفاده از ماکروی void bug(PyObject *list) { PyObject *item = PyList_GetItem(list, 0); Py_BEGIN_ALLOW_THREADS ...some blocking I/O call... Py_END_ALLOW_THREADS PyObject_Print(item, stdout, 0); /* BUG! */ } اشارهگرهای NULL¶بهطور کلی، توابعی که ارجاعهای شیء را بهعنوان آرگومان میگیرند، انتظار ندارند که اشارهگرهای بهتر است بررسی ماکروهای ماکروهای بررسی یک نوع شیء خاص ( مکانیسم فراخوانی تابع C تضمین میکند که فهرست آرگومانهای ارسالشده به توابع C (که در مثالها با این خطایی بسیار جدی است که اشارهگر نوشتن توسعهها با C++¶نوشتن ماژولهای توسعهای به زبان C++ ممکن است. برخی محدودیتها اعمال میشوند. اگر برنامه اصلی (مفسر پایتون) توسط کامپایلر C کامپایل و پیوند داده شود، نمیتوان از اشیاء سراسری یا ایستای دارای سازنده استفاده کرد. اگر برنامه اصلی توسط کامپایلر C++ پیوند داده شود، این مشکل وجود ندارد. توابعی که توسط مفسر پایتون فراخوانی خواهند شد (بهویژه توابع مقداردهی اولیه ماژول) باید با استفاده از ارائهی یک C API برای ماژول توسعهای¶بسیاری از ماژولهای توسعهای صرفاً توابع و نوعهای جدیدی فراهم میکنند که از پایتون مورد استفاده قرار میگیرند، اما گاهی کد موجود در یک ماژول توسعهای میتواند برای ماژولهای توسعهای دیگر مفید باشد. برای مثال، یک ماژول توسعهای میتواند نوعی به نام «مجموعه» پیادهسازی کند که مانند فهرستهای بدون ترتیب کار میکند. درست همانطور که نوع فهرست استاندارد پایتون دارای یک C API است که به ماژولهای توسعهای اجازه میدهد فهرستها را ایجاد و دستکاری کنند، این نوع مجموعه جدید نیز باید مجموعهای از توابع C برای دستکاری مستقیم از سوی ماژولهای توسعهای دیگر داشته باشد. در نگاه نخست این کار آسان به نظر میرسد: کافی است توابع را بنویسید (البته بدون اینکه آنها را بنابراین، قابلیت حمل (Portability) ایجاب میکند که هیچگونه فرضی دربارهی قابلیت دید نمادها (symbol visibility) در نظر نگیرید. این بدان معناست که برای جلوگیری از تداخل نام با سایر ماژولهای توسعهای، تمام نمادها در ماژولهای توسعهای باید به صورت پایتون سازوکار ویژهای برای انتقال اطلاعات در سطح C (اشارهگرها) از یک ماژول توسعهای به ماژول توسعهای دیگر فراهم میکند: کپسولها (Capsules). کپسول یک نوع داده پایتونی است که یک اشارهگر (void*) را ذخیره میکند. ایجاد و دسترسی به کپسولها تنها از طریق API زبان C آنها ممکن است، اما میتوان آنها را مانند هر شیء پایتونی دیگری منتقل کرد. بهطور خاص، میتوان آنها را به نامی در فضای نام یک ماژول توسعهای منتسب کرد. سپس ماژولهای توسعهای دیگر میتوانند این ماژول را ایمپورت کنند، مقدار این نام را بازیابی کنند و سپس اشارهگر را از کپسول بازیابی کنند. از کپسولها میتوان به روشهای بسیاری برای اکسپورت کردن API زبان C یک ماژول توسعهای استفاده کرد. هر تابع میتواند کپسول مخصوص خود را داشته باشد، یا تمام اشارهگرهای API زبان C میتوانند در آرایهای ذخیره شوند که آدرس آن در یک کپسول منتشر میشود. همچنین میتوان وظایف گوناگون ذخیره و بازیابی اشارهگرها را به شیوههای مختلف بین ماژولی که کد را فراهم میکند و ماژولهای مشتری توزیع کرد. هر روشی را که انتخاب کنید، مهم است که کپسولهای خود را بهدرستی نامگذاری کنید. تابع بهطور خاص، کپسولهایی که برای افشای APIهای C استفاده میشوند، باید مطابق این قرارداد نامگذاری شوند: modulename.attributename تابع کمکی مثال زیر رویکردی را نشان میدهد که بیشتر بار را بر دوش نویسنده ماژول اکسپورتکننده قرار میدهد، که برای ماژولهای کتابخانهای پرکاربرد مناسب است. این رویکرد تمام اشارهگرهای C API (در مثال فقط یکی!) را در آرایهای از اشارهگرهای void ذخیره میکند که مقدار یک کپسول میشود. پروندهی سرآیند متناظر با ماژول، ماکرویی را فراهم میکند که کار ایمپورت کردن ماژول و بازیابی اشارهگرهای C API آن را انجام میدهد؛ ماژولهای کلاینت تنها باید پیش از دسترسی به C API این ماکرو را فراخوانی کنند. ماژول صادرکننده، تغییری از ماژول تابع static int PySpam_System(const char *command) { return system(command); } تابع static PyObject * spam_system(PyObject *self, PyObject *args) { const char *command; int sts; if (!PyArg_ParseTuple(args, "s", &command)) return NULL; sts = PySpam_System(command); return PyLong_FromLong(sts); } در ابتدای ماژول، درست پس از سطر #include <Python.h> دو سطر دیگر باید افزوده شود: #define SPAM_MODULE #include "spammodule.h" از static int spam_module_exec(PyObject *m) { static void *PySpam_API[PySpam_API_pointers]; PyObject *c_api_object; /* Initialize the C API pointer array */ PySpam_API[PySpam_System_NUM] = (void *)PySpam_System; /* Create a Capsule containing the API pointer array's address */ c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL); if (PyModule_Add(m, "_C_API", c_api_object) < 0) { return -1; } return 0; } توجه داشته باشید که بخش عمدهی کار در پروندهی سرآیند #ifndef Py_SPAMMODULE_H #define Py_SPAMMODULE_H #ifdef __cplusplus extern "C" { #endif /* Header file for spammodule */ /* C API functions */ #define PySpam_System_NUM 0 #define PySpam_System_RETURN int #define PySpam_System_PROTO (const char *command) /* Total number of C API pointers */ #define PySpam_API_pointers 1 #ifdef SPAM_MODULE /* This section is used when compiling spammodule.c */ static PySpam_System_RETURN PySpam_System PySpam_System_PROTO; #else /* This section is used in modules that use spammodule's API */ static void **PySpam_API; #define PySpam_System \ (*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM]) /* Return -1 on error, 0 on success. * PyCapsule_Import will set an exception if there's an error. */ static int import_spam(void) { PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0); return (PySpam_API != NULL) ? 0 : -1; } #endif #ifdef __cplusplus } #endif #endif /* !defined(Py_SPAMMODULE_H) */ تنها کاری که یک ماژول کلاینت باید انجام دهد تا به تابع static int client_module_exec(PyObject *m) { if (import_spam() < 0) { return -1; } /* additional initialization can happen here */ return 0; } عیب اصلی این رویکرد این است که پروندهی در نهایت باید ذکر شود که Capsuleها عملکردهای اضافی ارائه میدهند، که بهویژه برای تخصیص و آزادسازی حافظه اشارهگر ذخیرهشده در یک Capsule مفید هستند. جزئیات در راهنمای ارجاع Python/C API در بخش کپسولها (Capsules) و در پیادهسازی Capsuleها (پروندههای پانوشتها
© حق نشر 2001 Python Software Foundation.
این صفحه تحت مجوز بنیاد نرمافزار پایتون نسخه ۲ منتشر شده است. مثالها، دستورالعملها، و سایر کدهای موجود در مستندات همچنین تحت مجوز بدون بند BSD منتشر شدهاند. برای اطلاعات بیشتر به تاریخچه و مجوز مراجعه کنید. بنیاد نرمافزار پایتون یک شرکت غیرانتفاعی است. لطفاً کمک مالی کنید. آخرین بهروزرسانی در نوامبر 12, 2026 (06:19 UTC). آیا اشکالی یافتید؟ ساخته شده با Sphinx 9.1.0. |
| You are visitor 01402520 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |