شاید یکی از مهمترین ساختارهای سیستم شیء پایتون، ساختاری باشد که یک نوع جدید را تعریف میکند: ساختار PyTypeObject. اشیاء نوع را میتوان با استفاده از هر یک از توابع PyObject_* یا PyType_* مدیریت کرد، اما چیز جالب زیادی برای بیشتر برنامههای پایتون ارائه نمیدهند. این اشیاء مبنای نحوه رفتار اشیاء هستند، بنابراین برای خود مفسر و برای هر ماژول توسعهای که نوعهای جدیدی را پیادهسازی میکند بسیار مهم هستند.
اشیاء نوع در مقایسه با اکثر نوعهای استاندارد نسبتاً بزرگ هستند. دلیل این اندازه آن است که هر شیء نوع تعداد زیادی مقدار را ذخیره میکند که عمدتاً اشارهگرهای تابع C هستند و هر یک بخش کوچکی از کارکرد نوع را پیادهسازی میکند. فیلدهای شیء نوع در این بخش به تفصیل بررسی میشوند. فیلدها به ترتیبی که در ساختار قرار دارند توصیف خواهند شد.
علاوه بر مرجع سریع زیر، بخش مثالها درکی سریع و فوری از معنا و کاربرد PyTypeObject فراهم میکند.
تعریف ساختار PyTypeObject را میتوان در Include/cpython/object.h یافت. برای سهولت مراجعه، این بخش تعریف موجود در آنجا را تکرار میکند:
typedefstruct_typeobject{PyObject_VAR_HEADconstchar*tp_name;/* For printing, in format "<module>.<name>" */Py_ssize_ttp_basicsize,tp_itemsize;/* For allocation *//* Methods to implement standard operations */destructortp_dealloc;Py_ssize_ttp_vectorcall_offset;getattrfunctp_getattr;setattrfunctp_setattr;PyAsyncMethods*tp_as_async;/* formerly known as tp_compare (Python 2) or tp_reserved (Python 3) */reprfunctp_repr;/* Method suites for standard classes */PyNumberMethods*tp_as_number;PySequenceMethods*tp_as_sequence;PyMappingMethods*tp_as_mapping;/* More standard operations (here for binary compatibility) */hashfunctp_hash;ternaryfunctp_call;reprfunctp_str;getattrofunctp_getattro;setattrofunctp_setattro;/* Functions to access object as input/output buffer */PyBufferProcs*tp_as_buffer;/* Flags to define presence of optional/expanded features */unsignedlongtp_flags;constchar*tp_doc;/* Documentation string *//* Assigned meaning in release 2.0 *//* call function for all accessible objects */traverseproctp_traverse;/* delete references to contained objects */inquirytp_clear;/* Assigned meaning in release 2.1 *//* rich comparisons */richcmpfunctp_richcompare;/* weak reference enabler */Py_ssize_ttp_weaklistoffset;/* Iterators */getiterfunctp_iter;iternextfunctp_iternext;/* Attribute descriptor and subclassing stuff */PyMethodDef*tp_methods;PyMemberDef*tp_members;PyGetSetDef*tp_getset;// Strong reference on a heap type, borrowed reference on a static typePyTypeObject*tp_base;PyObject*tp_dict;descrgetfunctp_descr_get;descrsetfunctp_descr_set;Py_ssize_ttp_dictoffset;initproctp_init;allocfunctp_alloc;newfunctp_new;freefunctp_free;/* Low-level free-memory routine */inquirytp_is_gc;/* For PyObject_IS_GC */PyObject*tp_bases;PyObject*tp_mro;/* method resolution order */PyObject*tp_cache;/* no longer used */void*tp_subclasses;/* for static builtin types this is an index */PyObject*tp_weaklist;/* not used for static builtin types */destructortp_del;/* Type attribute cache version tag. Added in version 2.6. * If zero, the cache is invalid and must be initialized. */unsignedinttp_version_tag;destructortp_finalize;vectorcallfunctp_vectorcall;/* bitset of which type-watchers care about this type */unsignedchartp_watched;/* Number of tp_version_tag values used. * Set to _Py_ATTR_CACHE_UNUSED if the attribute cache is * disabled for this type (e.g. due to custom MRO entries). * Otherwise, limited to MAX_VERSIONS_PER_CLASS (defined elsewhere). */uint16_ttp_versions_used;}PyTypeObject;
ساختار شیء نوع، ساختار PyVarObject را گسترش میدهد. فیلد ob_size برای نوعهای پویا استفاده میشود (ایجادشده توسط type_new() که معمولاً از یک دستور کلاس فراخوانی میشود). توجه داشته باشید که PyType_Type (فرانوع) مقدار tp_itemsize را مقداردهی اولیه میکند، که این بدان معناست که نمونههای آن (یعنی اشیاء نوع) باید فیلد ob_size را داشته باشند.
شمارش ارجاع شیء نوع توسط ماکروی PyObject_HEAD_INIT به 1 مقداردهی اولیه میشود. توجه داشته باشید که برای اشیای نوع تخصیصیافته بهصورت ایستا، نمونههای نوع (اشیایی که ob_type آنها به خود نوع اشاره میکند) بهعنوان ارجاع شمرده نمیشوند. اما برای اشیای نوع تخصیصیافته بهصورت پویا، نمونهها بهعنوان ارجاع شمرده میشوند.
این، نوعِ نوع است؛ به عبارت دیگر، فرانوع (metatype) آن است. این فیلد با آرگومان ماکروی PyObject_HEAD_INIT مقداردهی اولیه میشود و مقدار آن معمولاً باید &PyType_Type باشد. با این حال، برای ماژولهای توسعهای قابل بارگذاری پویا که باید در ویندوز (دستکم) قابل استفاده باشند، کامپایلر شکایت میکند که این یک مقداردهندهی اولیهی معتبر نیست. بنابراین، قرارداد بر این است که NULL به ماکروی PyObject_HEAD_INIT داده شود و این فیلد بهطور صریح در ابتدای تابع مقداردهی اولیهی ماژول، پیش از انجام هر کار دیگری، مقداردهی اولیه شود. این کار معمولاً به این شکل انجام میشود:
Foo_Type.ob_type=&PyType_Type;
این کار باید پیش از ایجاد هر نمونهای از این نوع انجام شود. PyType_Ready() بررسی میکند که آیا ob_type برابر NULL است یا خیر، و در این صورت، آن را به فیلد ob_type کلاس پایه مقداردهی اولیه میکند. PyType_Ready() در صورتی که این فیلد ناصفر باشد، آن را تغییر نمیدهد.
هر جایگاه بخشی دارد که وراثت را توصیف میکند. اگر ممکن است PyType_Ready() هنگامی که فیلد برابر NULL تنظیم شده باشد، مقداری تعیین کند، آنگاه بخشی نیز با عنوان «پیشفرض» وجود خواهد داشت. (توجه داشته باشید که بسیاری از فیلدهای تنظیمشده در PyBaseObject_Type و PyType_Type عملاً بهعنوان پیشفرض عمل میکنند.)
اشارهگر به رشتهی پایانیافته با تهی که نام نوع را در بر دارد. برای نوعهایی که بهصورت متغیرهای سراسری ماژول در دسترساند، این رشته باید شامل نام کامل ماژول، سپس یک نقطه و سپس نام نوع باشد؛ برای نوعهای توکار، باید فقط نام نوع باشد. اگر ماژول، زیرماژولی از یک بسته باشد، نام کامل بسته بخشی از نام کامل ماژول است. برای مثال، نوعی با نام T که در ماژول M در زیربستهی Q در بستهی P تعریفشده باشد، باید مقداردهندهی اولیهی tp_name آن "P.Q.M.T" باشد.
برای شیءهای نوعِ تخصیصیافته بهصورت پویا، این باید فقط نام نوع باشد و نام ماژول بهصورت صریح در دیکشنری نوع بهعنوان مقدار کلید '__module__' ذخیره شود.
اگر نقطهای وجود نداشته باشد، کل فیلد tp_name بهعنوان ویژگی __name__ در دسترس قرار میگیرد و ویژگی __module__ تعریفنشده است (مگر آنکه بهطور صریح در دیکشنری تنظیم شده باشد، همانطور که در بالا توضیح داده شد). این بدان معناست که پیکلکردن نوع شما ناممکن خواهد بود. علاوه بر این، در مستندات ماژولهایی که با pydoc ایجاد میشوند فهرست نخواهد شد.
دو گونه نوع وجود دارد: نوعهایی با نمونههای طول ثابت، فیلد tp_itemsize صفر دارند و نوعهایی با نمونههای طول متغیر، فیلد tp_itemsize غیرصفر دارند. برای نوعی با نمونههای طول ثابت، همه نمونهها اندازه یکسانی دارند که در tp_basicsize داده شده است. (میتوان با استفاده از PyUnstable_Object_GC_NewWithExtraData() برای این قاعده استثنا قائل شد.)
برای نوعی که نمونههای آن طول متغیر دارند، نمونهها باید دارای فیلد ob_size باشند و اندازهی نمونه برابر است با tp_basicsize بهاضافهی N برابر tp_itemsize، که در آن N «طول» شیء است.
توابعی مانند PyObject_NewVar() مقدار N را بهعنوان آرگومان میگیرند و آن را در فیلد ob_size نمونه ذخیره میکنند. توجه داشته باشید که فیلد ob_size ممکن است بعداً برای منظورهای دیگری استفاده شود. برای مثال، نمونههای int بیتهای ob_size را به شیوهای که توسط پیادهسازی تعریف شده است استفاده میکنند؛ برای دسترسی به ذخیرهسازی زیرین و اندازهی آن باید از PyLong_Export() استفاده کرد.
همچنین، وجود یک فیلد ob_size در چیدمان نمونه به این معنا نیست که ساختار نمونه طول متغیر دارد. برای مثال، نوع list نمونههایی با طول ثابت دارد، با این حال آن نمونهها فیلد ob_size دارند. (مانند int، از خواندن مستقیم ob_size فهرستها خودداری کنید. در عوض PyList_Size() را فراخوانی کنید.)
tp_basicsize شامل اندازهی مورد نیاز برای دادههای tp_base نوع، بهعلاوهی هر دادهی اضافی مورد نیاز برای هر نمونه است.
روش صحیح برای تنظیم tp_basicsize، استفاده از عملگر sizeof روی ساختاری است که برای اعلان چیدمان نمونه به کار میرود. این ساختار باید ساختاری را که برای اعلان نوع پایه به کار میرود، در بر بگیرد. به عبارت دیگر، tp_basicsize باید بزرگتر از یا برابر با tp_basicsize نوع پایه باشد.
tp_basicsize باید مضربی از _Alignof(PyObject) باشد. هنگام استفاده از sizeof بر روی یک struct که PyObject_HEAD را در بر میگیرد، همانطور که توصیه میشود، کامپایلر این موضوع را تضمین میکند. اگر از یک struct در C استفاده نکنید یا از افزونههای کامپایلر مانند __attribute__((packed)) استفاده کنید، این کار بر عهدهی شماست.
اگر آیتمهای متغیر به ترازبندی خاصی نیاز داشته باشند، tp_basicsize و tp_itemsize باید هر کدام مضربی از آن ترازبندی باشند. برای مثال، اگر بخش متغیرِ یک نوع، double ذخیره کند، مسئولیت شماست که هر دو فیلد مضربی از _Alignof(double) باشند.
ارثبری:
این فیلدها بهطور جداگانه توسط زیرنوعها به ارث برده میشوند. (یعنی اگر فیلد روی صفر تنظیم شده باشد، PyType_Ready() مقدار را از نوع پایه کپی میکند، که نشان میدهد نمونهها نیازی به فضای ذخیرهسازی اضافی ندارند.)
اگر نوع پایه دارای tp_itemsize ناصفر باشد، بهطور کلی ایمن نیست که tp_itemsize را در یک زیرنوع به مقدار ناصفر متفاوتی تنظیم کنید (هرچند این امر به پیادهسازی نوع پایه بستگی دارد).
اشارهگری به تابع مخرب نمونه. امضای تابع به صورت زیر است:
voidtp_dealloc(PyObject*self);
تابع مخرب باید تمام ارجاعهایی را که نمونه مالک آنهاست حذف کند (مثلاً Py_CLEAR() را فراخوانی کند)، تمام بافرهای حافظهای را که در مالکیت نمونهاند آزاد کند و برای آزاد کردن خودِ شیء، تابع tp_free نوع را فراخوانی کند.
اگر ممکن است توابعی را فراخوانی کنید که نشانگر خطا را تنظیم کنند، باید از PyErr_GetRaisedException() و PyErr_SetRaisedException() استفاده کنید تا مطمئن شوید نشانگر خطای از پیش موجود را از بین نمیبرید (آزادسازی ممکن است هنگام پردازش خطایی دیگر رخ داده باشد):
خودِ هندلر تخصیصزدایی (dealloc) نباید استثنایی پرتاب کند؛ اگر با مورد خطایی مواجه شود، باید PyErr_FormatUnraisable() را فراخوانی کند تا استثنای غیرقابلپرتاب (unraisable) را گزارش (و پاک) کند.
هیچ تضمینی وجود ندارد که یک شیء چه زمانی نابود میشود، مگر:
پایتون یک شیء را بلافاصله یا مدتی پس از حذف آخرین ارجاع به آن نابود میکند، مگر آنکه نهاییساز آن (tp_finalize) متعاقباً شیء را احیا کند.
یک شیء تا زمانی که در حال نهاییسازی خودکار (tp_finalize) یا پاکسازی خودکار (tp_clear) است، نابود نخواهد شد.
سیپایتون در حال حاضر یک شیء را هنگامی که شمارش ارجاع جدید صفر است، بلافاصله از طریق Py_DECREF() نابود میکند، اما این ممکن است در نسخهای در آینده تغییر کند.
توصیه میشود که PyObject_CallFinalizerFromDealloc() در ابتدای tp_dealloc فراخوانی شود تا تضمین شود که شیء همیشه پیش از تخریب نهاییسازی میشود.
اگر نوع از زبالهروبی پشتیبانی کند (پرچم Py_TPFLAGS_HAVE_GC تنظیم شده باشد)، مخرب باید پیش از پاک کردن هر یک از فیلدهای عضو، PyObject_GC_UnTrack() را فراخوانی کند.
مجاز است که tp_clear را از tp_dealloc فراخوانی کنید تا از تکرار کد کاسته شود و تضمین شود که شیء همیشه پیش از انهدام پاکسازی میشود. مراقب باشید که ممکن است tp_clear از پیش فراخوانی شده باشد.
اگر نوع در هیپ تخصیصیافته باشد (Py_TPFLAGS_HEAPTYPE)، تخصیصگشا باید پس از فراخوانی تخصیصگشای نوع، ارجاع مالکانه به شیء نوع خود را (از طریق Py_DECREF()) آزاد کند. کد نمونه را در ادامه ببینید.:
tp_dealloc باید وضعیت استثنا را بدون تغییر بگذارد. اگر لازم باشد چیزی را فراخوانی کند که ممکن است استثنا ایجاد کند، وضعیت استثنا باید ابتدا پشتیبانگیری و بعداً بازگردانی شود (پس از گزارشگیری هرگونه استثنا با PyErr_WriteUnraisable()).
مثال:
staticvoidfoo_dealloc(PyObject*self){PyObject*exc=PyErr_GetRaisedException();if(PyObject_CallFinalizerFromDealloc(self)<0){// self was resurrected.gotodone;}PyTypeObject*tp=Py_TYPE(self);if(tp->tp_flags&Py_TPFLAGS_HAVE_GC){PyObject_GC_UnTrack(self);}// Optional, but convenient to avoid code duplication.if(tp->tp_clear&&tp->tp_clear(self)<0){PyErr_WriteUnraisable(self);}// Any additional destruction goes here.tp->tp_free(self);self=NULL;// In case PyErr_WriteUnraisable() is called below.if(tp->tp_flags&Py_TPFLAGS_HEAPTYPE){Py_CLEAR(tp);}done:// Optional, if something was called that might have raised an// exception.if(PyErr_Occurred()){PyErr_WriteUnraisable(self);}PyErr_SetRaisedException(exc);}
tp_dealloc ممکن است از هر نخ پایتون فراخوانی شود، نه فقط از نخی که شیء را ایجاد کرده است (اگر شیء بخشی از یک چرخه شمارش ارجاع شود، ممکن است آن چرخه توسط یک زبالهروبی روی هر نخ جمعآوری شود). این برای فراخوانیهای API پایتون مشکلی نیست، زیرا نخی که tp_dealloc روی آن فراخوانی میشود، دارای وضعیت نخ متصل است. با این حال، اگر شیءِ در حال نابود شدن به نوبه خود اشیایی از یک کتابخانه C دیگر را نابود کند، باید دقت کنید تا مطمئن شوید که نابود کردن آن اشیا روی نخی که tp_dealloc را فراخوانی کرده است، هیچیک از فرضهای آن کتابخانه را نقض نخواهد کرد.
ارثبری:
این فیلد به زیرنوعها به ارث میرسد.
همچنین ببینید
چرخهی حیات شیء را برای جزئیات دربارهی نحوهی ارتباط این جایگاه با جایگاههای دیگر ببینید.
آفست اختیاری برای تابعی بهازای هر نمونه که فراخوانی شیء را با استفاده از پروتکل vectorcall پیادهسازی میکند؛ این پروتکل جایگزینی کارآمدتر برای tp_call سادهتر است.
این فیلد تنها زمانی استفاده میشود که پرچم Py_TPFLAGS_HAVE_VECTORCALL تنظیم شده باشد. در این صورت، این فیلد باید یک عدد صحیح مثبت حاوی آفست اشارهگر vectorcallfunc در نمونه باشد.
اشارهگر vectorcallfunc ممکن است NULL باشد، که در این صورت نمونه به گونهای رفتار میکند که گویی Py_TPFLAGS_HAVE_VECTORCALL تنظیم نشده است: فراخوانی نمونه به tp_call بازمیگردد.
هر کلاسی که Py_TPFLAGS_HAVE_VECTORCALL را تنظیم میکند، باید tp_call را نیز تنظیم کند و اطمینان حاصل کند که رفتار آن با تابع vectorcallfunc سازگار است. این کار را میتوان با تنظیم tp_call برابر با PyVectorcall_Call() انجام داد.
تغییر یافته در نسخهی 3.8: پیش از نسخه 3.8، این جایگاه tp_print نام داشت. در پایتون 2.x، از آن برای چاپ در یک پرونده استفاده میشد. در پایتون 3.0 تا 3.7، استفاده نمیشد.
تغییر یافته در نسخهی 3.12: پیش از نسخه 3.12، توصیه نمیشد که نوعهای هیپِ تغییرپذیر پروتکل vectorcall را پیادهسازی کنند. هنگامی که کاربر __call__ را در کد پایتون تنظیم میکند، تنها tp_call بهروزرسانی میشود که احتمالاً آن را با تابع vectorcall ناسازگار میسازد. از نسخه 3.12 به بعد، تنظیم __call__ بهینهسازی vectorcall را با پاک کردن پرچم Py_TPFLAGS_HAVE_VECTORCALL غیرفعال میکند.
ارثبری:
این فیلد همیشه به ارث برده میشود. با این حال، پرچم Py_TPFLAGS_HAVE_VECTORCALL همیشه به ارث برده نمیشود. اگر این پرچم تنظیم نشده باشد، زیرکلاس از vectorcall استفاده نخواهد کرد، مگر اینکه PyVectorcall_Call() بهطور صریح فراخوانی شود.
این فیلد منسوخ شده است. هنگامی که تعریف شود، باید به تابعی اشاره کند که مانند تابع tp_getattro عمل میکند، اما بهجای شیء رشته پایتون، یک رشته C برای ارائهی نام ویژگی میگیرد.
این فیلد به همراه tp_getattro توسط زیرنوعها به ارث میرسد: یک زیرنوع، هر دو tp_getattr و tp_getattro را از نوع پایه خود به ارث میبرد، زمانی که tp_getattr و tp_getattro زیرنوع هر دو NULL باشند.
یک اشارهگر اختیاری به تابعِ تنظیم و حذف ویژگیها.
این فیلد منسوخ شده است. هنگامی که تعریف میشود، باید به تابعی اشاره کند که مانند تابع tp_setattro عمل میکند، اما به جای شیء رشتهی پایتون، یک رشتهی C برای ارائهی نام ویژگی میپذیرد.
این فیلد همراه با tp_setattro توسط زیرنوعها به ارث برده میشود: یک زیرنوع هر دو tp_setattr و tp_setattro را از نوع پایهی خود به ارث میبرد وقتی که tp_setattr و tp_setattro آن هر دو NULL باشند.
اشارهگر به یک ساختار اضافی که فیلدهایی را در بر میگیرد که تنها به اشیایی مربوطاند که پروتکلهای awaitable و asynchronous iterator را در سطح C پیادهسازی میکنند. برای جزئیات به ساختارهای اشیای ناهمگام مراجعه کنید.
اضافه شده در نسخهی 3.5: قبلاً با نام tp_compare و tp_reserved شناخته میشد.
ارثبری:
فیلد tp_as_async به ارث نمیرسد، اما فیلدهای درون آن بهصورت جداگانه به ارث میرسند.
این تابع باید یک رشته یا یک شیء یونیکد را برگرداند. در حالت ایدهآل، این تابع باید رشتهای را برگرداند که وقتی به eval() پاس داده شود، با فراهم بودن محیطی مناسب، شیءای با همان مقدار را بازگرداند. اگر این کار امکانپذیر نباشد، باید رشتهای را برگرداند که با '<' شروع میشود و به '>' ختم میشود و از روی آن میتوان هم نوع و هم مقدار شیء را استنباط کرد.
ارثبری:
این فیلد به زیرنوعها به ارث میرسد.
پیشفرض:
هنگامی که این فیلد تنظیم نشده باشد، رشتهای به شکل <%sobjectat%p> برگردانده میشود که در آن %s با نام نوع و %p با نشانی حافظهی شیء جایگزین میشود.
اشارهگر به یک ساختار اضافی که فیلدهایی را در بر دارد که تنها برای اشیایی که پروتکل عددی را پیادهسازی میکنند مربوطاند. این فیلدها در ساختارهای شیء عدد مستند شدهاند.
ارثبری:
فیلد tp_as_number به ارث برده نمیشود، اما فیلدهای درون آن بهصورت جداگانه به ارث برده میشوند.
اشارهگر به یک ساختار اضافی که فیلدهایی را در بر میگیرد که تنها به اشیائی مربوطاند که پروتکل دنباله را پیادهسازی میکنند. این فیلدها در ساختارهای اشیاء دنباله مستند شدهاند.
ارثبری:
فیلد tp_as_sequence به ارث برده نمیشود، اما فیلدهای درون آن بهصورت جداگانه به ارث برده میشوند.
مقدار -1 نباید بهعنوان یک مقدار بازگشتی عادی برگردانده شود؛ هنگامی که در حین محاسبهی مقدار هش خطایی رخ میدهد، تابع باید یک استثنا تنظیم کرده و -1 را برگرداند.
این فیلد را میتوان بهطور صریح روی PyObject_HashNotImplemented() تنظیم کرد تا از ارثبری متد هش از نوع والد جلوگیری شود. این بهعنوان معادل __hash__=None در سطح پایتون تفسیر میشود و باعث میشود isinstance(o,collections.Hashable) بهدرستی False را برگرداند. توجه داشته باشید که عکس این موضوع نیز صادق است — تنظیم __hash__=None روی یک کلاس در سطح پایتون باعث میشود جایگاه tp_hash روی PyObject_HashNotImplemented() تنظیم شود.
این فیلد به همراه tp_richcompare توسط زیرنوعها به ارث برده میشود: یک زیرنوع هر دو tp_richcompare و tp_hash را به ارث میبرد، زمانی که tp_richcompare و tp_hash زیرنوع هر دو NULL باشند.
اشارهگر اختیاری به تابعی که فراخوانی شیء را پیادهسازی میکند. اگر شیء فراخوانیپذیر نباشد، این اشارهگر باید NULL باشد. امضای این تابع همانند PyObject_Call() است:
یک اشارهگر اختیاری به تابعی که عملیات توکار str() را پیادهسازی میکند. (توجه داشته باشید که str اکنون یک نوع است و str() سازندهی آن نوع را فراخوانی میکند. این سازنده برای انجام کار اصلی PyObject_Str() را فراخوانی میکند و PyObject_Str() این هندلر را فراخوانی خواهد کرد.)
این تابع باید یک رشته یا یک شیء یونیکد را برگرداند. این باید نمایش رشتهای «دوستانه» از شیء باشد، زیرا این نمایشی است که، از جمله موارد دیگر، توسط تابع print() استفاده خواهد شد.
ارثبری:
این فیلد به زیرنوعها به ارث میرسد.
پیشفرض:
هنگامی که این فیلد تنظیمنشده باشد، PyObject_Repr() برای برگرداندن یک نمایش رشتهای فراخوانی میشود.
این فیلد همراه با tp_getattr توسط زیرنوعها به ارث برده میشود: زیرنوع زمانی هر دو tp_getattr و tp_getattro را از نوع پایهی خود به ارث میبرد که هر دو tp_getattr و tp_getattro آن NULL باشند.
علاوه بر این، باید از تنظیم value به NULL برای حذف یک ویژگی پشتیبانی شود. معمولاً سودمند است که این فیلد را به PyObject_GenericSetAttr() تنظیم کنید که روش معمول تنظیم ویژگیهای شیء را پیادهسازی میکند.
این فیلد به همراه tp_setattr توسط زیرنوعها به ارث برده میشود: زیرنوعی که tp_setattr و tp_setattro آن هر دو NULL هستند، هر دو tp_setattr و tp_setattro را از نوع پایه خود به ارث میبرد.
اشارهگر به یک ساختار اضافی که فیلدهای آن تنها برای اشیایی که رابط بافر را پیادهسازی میکنند مربوط است. این فیلدها در ساختارهای شیء بافر مستند شدهاند.
ارثبری:
فیلد tp_as_buffer به ارث برده نمیشود، اما فیلدهای درون آن بهطور جداگانه به ارث برده میشوند.
این فیلد یک نقاب بیتی از پرچمهای گوناگون است. برخی پرچمها معناشناسی متفاوتی را برای موقعیتهای خاص نشان میدهند؛ برخی دیگر برای نشان دادن این موضوع به کار میروند که فیلدهای خاصی در شیء نوع (یا در ساختارهای توسعهای که از طریق tp_as_number، tp_as_sequence، tp_as_mapping و tp_as_buffer به آنها ارجاع میشود) که بهطور تاریخی همیشه وجود نداشتهاند، معتبر هستند؛ اگر چنین بیت پرچمی صفر باشد، نباید به فیلدهای نوعی که این بیت از آنها نگهبانی میکند دسترسی پیدا کرد و باید در عوض، آنها را دارای مقدار صفر یا NULL در نظر گرفت.
ارثبری:
وراثت این فیلد پیچیده است. بیشتر بیتهای پرچم بهصورت جداگانه به ارث میرسند، یعنی اگر بیت پرچمی در نوع پایه تنظیمشده باشد، زیرنوع این بیت پرچم را به ارث میبرد. بیتهای پرچم مربوط به ساختارهای توسعهای، در صورتی که ساختار توسعهای به ارث برسد، بهطور اکید به ارث میرسند؛ یعنی مقدار بیت پرچم در نوع پایه به همراه اشارهگری به ساختار توسعهای، در زیرنوع کپی میشود. بیت پرچم Py_TPFLAGS_HAVE_GC همراه با فیلدهای tp_traverse و tp_clear به ارث میرسد، یعنی بیت پرچم Py_TPFLAGS_HAVE_GC در صورتی به ارث میرسد که در زیرنوع صفر باشد و فیلدهای tp_traverse و tp_clear در زیرنوع موجود باشند و مقدار NULL داشته باشند.
پیشفرض:
PyBaseObject_Type از Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE استفاده میکند.
نقابهای بیتی:
نقابهای بیتی زیر در حال حاضر تعریف شدهاند؛ این نقابها را میتوان با استفاده از عملگر | با یکدیگر OR کرد تا مقدار فیلد tp_flags را تشکیل دهند. ماکروی PyType_HasFeature() یک نوع و یک مقدار پرچم، یعنی tp و f، میگیرد و بررسی میکند که آیا tp->tp_flags&f ناصفر است یا خیر.
این بیت زمانی تنظیم میشود که خودِ شیء نوع روی هیپ تخصیص یابد؛ برای مثال، نوعهایی که بهصورت پویا با استفاده از PyType_FromSpec() ایجاد میشوند. در این حالت، فیلد ob_type نمونههای آن بهعنوان ارجاعی به نوع در نظر گرفته میشود و شیء نوع هنگام ایجاد شدن یک نمونهی جدید INCREF میشود و هنگام نابود شدن نمونه DECREF میشود (این مورد برای نمونههای زیرنوعها اعمال نمیشود؛ فقط نوعی که ob_type نمونه به آن ارجاع میدهد INCREF یا DECREF میشود). نوعهای هیپ همچنین باید زبالهروبی را پشتیبانی کنند، زیرا میتوانند با شیء ماژول خود چرخهی ارجاع تشکیل دهند.
این بیت زمانی تنظیم میشود که بتوان از این نوع بهعنوان نوع پایهی نوع دیگری استفاده کرد. اگر این بیت پاک باشد، نمیتوان از این نوع زیرنوعی ساخت (مشابه کلاس «final» در جاوا).
بیت پرچم Py_TPFLAGS_HAVE_GC به همراه فیلدهای tp_traverse و tp_clear به ارث برده میشود؛ یعنی اگر بیت پرچم Py_TPFLAGS_HAVE_GC در زیرنوع صفر باشد و فیلدهای tp_traverse و tp_clear در زیرنوع وجود داشته باشند و مقدار NULL داشته باشند.
این نقاب بیتی تمام بیتهایی را در بر میگیرد که به وجود فیلدهای معین در شیء نوع و ساختارهای توسعهای آن مربوط میشوند. در حال حاضر، شامل بیتهای زیر است: Py_TPFLAGS_HAVE_STACKLESS_EXTENSION.
این بیت نشان میدهد که اشیاء مانند متدهای غیرمقید رفتار میکنند.
اگر این پرچم برای type(meth) تنظیمشده باشد، آنگاه:
عبارت meth.__get__(obj,cls)(*args,**kwds) (وقتی obj برابر None نیست) باید معادل meth(obj,*args,**kwds) باشد.
meth.__get__(None,cls)(*args,**kwds) باید معادل meth(*args,**kwds) باشد.
این پرچم بهینهسازیای را برای فراخوانیهای متداول متد مانند obj.meth() فعال میکند: از ایجاد یک شیء موقت «متد مقید» برای obj.meth جلوگیری میکند.
اضافه شده در نسخهی 3.8.
ارثبری:
این پرچم هرگز توسط نوعهایی که پرچم Py_TPFLAGS_IMMUTABLETYPE روی آنها تنظیمنشده است، به ارث برده نمیشود. برای نوعهای توسعهای، این پرچم هر زمان که tp_descr_get به ارث برده شود، به ارث برده میشود.
این بیتها نشان میدهند که ماشین مجازی (VM) برخی از فیلدها را با ذخیرهسازی آنها پیش از شیء مدیریت خواهد کرد. در حال حاضر، این کلاندستور معادل Py_TPFLAGS_MANAGED_DICT|Py_TPFLAGS_MANAGED_WEAKREF است.
این مقدار کلاندستور به پیادهسازی ماشین مجازی (VM) وابسته است؛ بنابراین، مقدار آن پایدار نیست و ممکن است در نسخهی آینده تغییر کند. ترجیح بر این است که بهجای آن از پرچمهای مجزا استفاده کنید.
فقط با نوعهای دارای اندازه متغیر قابل استفاده است؛ یعنی نوعهایی که tp_itemsize غیر صفر دارند.
نشان میدهد که بخش با اندازهی متغیرِ نمونهای از این نوع، در انتهای ناحیهی حافظهی نمونه و با آفست Py_TYPE(obj)->tp_basicsize قرار دارد (که ممکن است در هر زیرکلاس متفاوت باشد).
هنگام تنظیم این پرچم، مطمئن شوید که همهی ابرکلاسها یا از این چیدمان حافظه استفاده میکنند یا اندازهی متغیر ندارند. پایتون این مورد را بررسی نمیکند.
توابعی مانند PyLong_Check() با یکی از این پرچمها PyType_FastSubclass() را فراخوانی میکنند تا بهسرعت تعیین کنند که آیا یک نوع، زیرکلاس یک نوع توکار است یا نه؛ چنین بررسیهای خاصی سریعتر از یک بررسی عام مانند PyObject_IsInstance() هستند. نوعهای سفارشی که از نوعهای توکار ارثبری میکنند باید tp_flags خود را بهدرستی تنظیم کنند، وگرنه کدی که با چنین نوعهایی تعامل دارد، بسته به اینکه چه نوع بررسیای استفاده میشود، رفتار متفاوتی خواهد داشت.
ایجاد نمونه از این نوع را ممنوع کنید: tp_new را برابر NULL قرار دهید و کلید __new__ را در دیکشنری نوع ایجاد نکنید.
پرچم باید پیش از ایجاد نوع تنظیم شود، نه پس از آن. برای مثال، باید پیش از فراخوانی PyType_Ready() روی نوع تنظیم شود.
اگر tp_base برابر NULL یا &PyBaseObject_Type باشد و tp_new برابر NULL باشد، این پرچم بهطور خودکار بر روی نوعهای ایستا تنظیم میشود.
ارثبری:
این پرچم به ارث برده نمیشود. با این حال، زیرکلاسها قابل نمونهسازی نخواهند بود مگر آنکه یک tp_new غیر NULL ارائه کنند (که این کار تنها از طریق API زبان C امکانپذیر است).
نکته
برای اینکه نمونهسازی مستقیم از یک کلاس ممنوع باشد اما نمونهسازی از زیرکلاسهای آن مجاز باشد (مثلاً برای یک کلاس پایه انتزاعی)، از این پرچم استفاده نکنید. در عوض، کاری کنید که tp_new تنها برای زیرکلاسها موفق شود.
این بیت نشان میدهد که نمونههای کلاس، زمانی که بهعنوان موضوع بلوک match استفاده میشوند، میتوانند با الگوهای نگاشت تطبیق یابند. این بیت هنگام ثبت یا زیرکلاسسازی collections.abc.Mapping بهطور خودکار تنظیم میشود و هنگام ثبت collections.abc.Sequence از تنظیم خارج میشود.
این بیت نشان میدهد که نمونههای کلاس ممکن است هنگامی که بهعنوان موضوع یک بلوک match استفاده میشوند، با الگوهای دنباله تطبیق یابند. این بیت بهطور خودکار هنگام ثبت کردن یا زیرکلاسسازی collections.abc.Sequence تنظیم میشود و هنگام ثبت کردن collections.abc.Mapping پاک میشود.
این بیت نشان میدهد که نمونههای این نوع، آرایهی «مقادیر درونخطی» (inline values) — شامل ویژگیهای شیء — خواهند داشت که مستقیماً پس از انتهای شیء قرار میگیرد.
یک اشارهگر اختیاری به رشتهی C پایانیافته با تهی که رشتهی مستند این شیء نوع را ارائه میدهد. این رشتهی مستند بهصورت ویژگی __doc__ روی نوع و نمونههای آن نوع در دسترس قرار میگیرد.
این فیلد به همراه tp_clear و بیت پرچم Py_TPFLAGS_HAVE_GC توسط زیرنوعها به ارث برده میشود: بیت پرچم، tp_traverse و tp_clear همگی در صورتی از نوع پایه به ارث برده میشوند که هر سه در زیرنوع صفر باشند.
اشارهگر اختیاری به تابع پاکسازی. امضا به این صورت است:
inttp_clear(PyObject*);
هدف از این تابع، شکستن چرخههای ارجاعی است که یک cyclic isolate ایجاد میکنند تا اشیاء بتوانند بهطور ایمن نابود شوند. شیء پاکسازیشده شیءای است که بهطور جزئی نابود شده است؛ این شیء موظف نیست ناوردهای طراحیای را که در استفادهی عادی برقرارند رعایت کند.
tp_clear نیازی به حذف ارجاعات به اشیایی ندارد که نمیتوانند در چرخههای ارجاع شرکت کنند، مانند رشتههای پایتون یا اعداد صحیح پایتون. با این حال، ممکن است پاککردن همهی ارجاعات و نوشتن تابع tp_dealloc نوع بهگونهای که tp_clear را فراخوانی کند، برای جلوگیری از تکرار کد مناسب باشد. (مراقب باشید که ممکن است tp_clear قبلاً فراخوانی شده باشد. فراخوانی توابع همتوان مانند Py_CLEAR() را ترجیح دهید.)
هرگونه پاکسازی غیرساده باید بهجای tp_clear در tp_finalize انجام شود.
نکته
اگر tp_clear نتواند یک چرخه ارجاع را بشکند، ممکن است اشیاء درون cyclic isolate بهطور نامحدود غیرقابل جمعآوری باقی بمانند («نشت»). gc.garbage را ببینید.
نکته
ارجاعشوندهها (referents) — مستقیم و غیرمستقیم — ممکن است از پیش پاکسازی شده باشند؛ تضمینی نیست که در وضعیتی سازگار باشند.
تضمین نمیشود که یک شیء پیش از فراخوانی مخرب آن (tp_dealloc) بهطور خودکار پاکسازی شود.
این تابع به روشهای زیر با مخرب (tp_dealloc) تفاوت دارد:
هدف از پاکسازی یک شیء، حذف ارجاعها به اشیاء دیگری است که ممکن است در چرخه ارجاع شرکت کنند. از سوی دیگر، هدف مخرب ابرمجموعهای از آن است: مخرب باید همه منابعی را که در اختیار دارد آزاد کند، از جمله ارجاعها به اشیائی که نمیتوانند در چرخه ارجاع شرکت کنند (مانند اعداد صحیح) و همچنین حافظهی خودِ شیء (با فراخوانی tp_free).
وقتی tp_clear فراخوانی میشود، ممکن است اشیاء دیگر همچنان ارجاعهایی به شیءِ در حال پاکشدن داشته باشند. به همین دلیل، tp_clear نباید حافظهی خودِ شیء را آزاد کند (tp_free). از سوی دیگر، مخرب تنها زمانی فراخوانی میشود که هیچ ارجاعی (قوی) وجود نداشته باشد، و از این رو، باید خودِ شیء را با آزاد کردن آن بهطور ایمن نابود کند.
tp_clear ممکن است هرگز بهطور خودکار فراخوانی نشود. از سوی دیگر، مخرب یک شیء، مدتی پس از دسترسناپذیر شدن شیء بهطور خودکار فراخوانی میشود (یعنی، یا هیچ ارجاعی به شیء وجود ندارد یا شیء عضوی از یک cyclic isolate است).
هیچ تضمینی دربارهی اینکه پایتون چه زمانی، آیا اصلاً، یا هر چند وقت یکبار یک شیء را بهطور خودکار پاک میکند ارائه نمیشود، مگر در موارد زیر:
پایتون بهطور خودکار شیءای را پاک نمیکند که دسترسیپذیر باشد؛ یعنی ارجاعی به آن وجود داشته باشد و عضوی از cyclic isolate نباشد.
اگر یک شیء بهطور خودکار نهاییسازی نشده باشد، پایتون آن را بهطور خودکار پاک نمیکند (به tp_finalize مراجعه کنید). (اگر نهاییساز شیء را احیا کرده باشد، ممکن است شیء پیش از پاک شدن، دوباره بهطور خودکار نهاییسازی شود یا نشود.)
اگر شیءای عضوی از یک cyclic isolate باشد، پایتون در صورتی که عضوی از cyclic isolate هنوز بهطور خودکار نهاییسازی نشده باشد، آن را بهطور خودکار پاک نمیکند (tp_finalize).
پایتون تا پس از بازگشت هرگونه فراخوانی خودکار تابع tp_clear یک شیء، آن را نابود نمیکند. این تضمین میکند که عمل شکستن چرخه ارجاع، اشارهگر self را در حالی که tp_clear هنوز در حال اجراست، نامعتبر نکند.
پایتون بهطور خودکار tp_clear را چندین بار بهطور همزمان فراخوانی نمیکند.
سیپایتون در حال حاضر اشیاء را تنها زمانی بهطور خودکار پاکسازی میکند که برای شکستن چرخههای ارجاع در یک cyclic isolate نیاز باشد، اما نسخههای آینده ممکن است اشیاء را بهطور منظم پیش از تخریبشان پاکسازی کنند.
در مجموع، همهی توابع tp_clear در سیستم باید با هم تمام چرخههای ارجاع را بشکنند. این موضوع ظریف است، و در صورت وجود هرگونه شک، یک تابع tp_clear ارائه دهید. برای مثال، نوع تاپل تابع tp_clear را پیادهسازی نمیکند، زیرا میتوان اثبات کرد که هیچ چرخهی ارجاعی نمیتواند بهطور کامل از تاپلها تشکیل شود. بنابراین توابع tp_clear انواع دیگر مسئول شکستن هر چرخهای هستند که شامل یک تاپل باشد. این موضوع در نگاه اول آشکار نیست، و بهندرت دلیل خوبی برای اجتناب از پیادهسازی tp_clear وجود دارد.
پیادهسازیهای tp_clear باید ارجاعهای نمونه به آنهایی از اعضایش که ممکن است اشیاء پایتون باشند را رها کنند و اشارهگرهایش به آن اعضا را برابر NULL قرار دهند، همانند مثال زیر:
باید از ماکروی Py_CLEAR() استفاده کرد، زیرا پاکسازی ارجاعها امری ظریف است: ارجاع به شیء درونگرفته نباید (از طریق Py_DECREF()) آزاد شود، مگر پس از آنکه اشارهگر به شیء درونگرفته برابر NULL قرار گرفته باشد. دلیل این امر آن است که آزاد کردن ارجاع ممکن است باعث شود شیء درونگرفته به زباله تبدیل شود و زنجیرهای از فعالیت بازیافت را فعال کند که ممکن است شامل فراخوانی کد دلخواه پایتون باشد (به دلیل نهاییسازها یا کالبکهای weakref مرتبط با شیء درونگرفته). اگر چنین کدی بتواند دوباره به self ارجاع کند، مهم است که اشارهگر به شیء درونگرفته در آن لحظه برابر NULL باشد، تا self بداند که شیء درونگرفته دیگر قابل استفاده نیست. ماکروی Py_CLEAR() عملیاتها را به ترتیبی ایمن انجام میدهد.
این فیلد به همراه tp_traverse و بیت پرچم Py_TPFLAGS_HAVE_GC توسط زیرنوعها به ارث برده میشود: اگر بیت پرچم، tp_traverse و tp_clear در زیرنوع همگی صفر باشند، از نوع پایه به ارث برده میشوند.
همچنین ببینید
چرخهی حیات شیء را برای جزئیات دربارهی نحوهی ارتباط این جایگاه با جایگاههای دیگر ببینید.
تضمین میشود که پارامتر اول، نمونهای از نوعی باشد که توسط PyTypeObject تعریف شده است.
تابع باید نتیجهی مقایسه را برگرداند (معمولاً Py_True یا Py_False). اگر مقایسه تعریفنشده باشد، باید Py_NotImplemented را برگرداند، و اگر خطای دیگری رخ داده باشد، باید NULL برگرداند و یک وضعیت استثنا را تنظیم کند.
بسته به نتیجهی مقایسه، Py_True یا Py_False را از تابع برگردانید. VAL_A و VAL_B باید با عملگرهای مقایسهی C قابل مرتبسازی باشند (برای مثال، ممکن است اعداد صحیح یا ممیز شناور در C باشند). آرگومان سوم، عملیات درخواستی را مشخص میکند، همانگونه که برای PyObject_RichCompare() است.
این فیلد به همراه tp_hash توسط زیرنوعها به ارث برده میشود: زیرنوع زمانی tp_richcompare و tp_hash را به ارث میبرد که هر دو tp_richcompare و tp_hash آن NULL باشند.
پیشفرض:
PyBaseObject_Type یک پیادهسازی از tp_richcompare فراهم میکند که ممکن است به ارث برده شود. با این حال، اگر تنها tp_hash تعریف شده باشد، حتی تابع بهارثرسیده هم استفاده نمیشود و نمونههای این نوع نمیتوانند در هیچ مقایسهای شرکت کنند.
هرچند این فیلد همچنان پشتیبانی میشود، تا جای ممکن باید به جای آن از Py_TPFLAGS_MANAGED_WEAKREF استفاده شود.
اگر نمونههای این نوع قابل ارجاع ضعیف باشند، این فیلد بزرگتر از صفر است و آفست سر فهرست ارجاعهای ضعیف در ساختار نمونه را در بر میگیرد (با نادیده گرفتن سرآیند GC، در صورت وجود)؛ این آفست توسط PyObject_ClearWeakRefs() و توابع PyWeakref_* استفاده میشود. ساختار نمونه باید شامل یک فیلد از نوع PyObject* باشد که با NULL مقداردهی اولیه میشود.
این فیلد را با tp_weaklist اشتباه نگیرید؛ آن، سر فهرست ارجاعهای ضعیف به خودِ شیء نوع است.
این فیلد توسط زیرنوعها به ارث برده میشود، اما به قواعد فهرستشده در پایین مراجعه کنید. یک زیرنوع میتواند این آفست را بازنویسی کند؛ این بدان معناست که زیرنوع از سر فهرست ارجاعهای ضعیف متفاوتی نسبت به نوع پایه استفاده میکند. از آنجا که سر فهرست همیشه از طریق tp_weaklistoffset یافت میشود، این نباید مشکلی ایجاد کند.
یک اشارهگر اختیاری به تابعی که یک iterator برای شیء برمیگرداند. حضور آن معمولاً نشان میدهد که نمونههای این نوع iterable هستند (هرچند دنبالهها ممکن است بدون این تابع پیمایشپذیر باشند).
اشارهگر اختیاری به تابعی که آیتم بعدی در یک پیمایشگر را برمیگرداند. امضا به این صورت است:
PyObject*tp_iternext(PyObject*self);
وقتی پیمایشگر به پایان رسیده باشد، تابع tp_iternext باید NULL برگرداند؛ یک استثنای StopIteration ممکن است تنظیم شده باشد یا خیر. وقتی خطای دیگری رخ میدهد، نیز باید NULL برگرداند. وجود tp_iternext سیگنال میدهد که نمونههای این نوع پیمایشگر هستند.
نوعهای پیمایشگر باید تابع tp_iter را نیز تعریف کنند، و این تابع باید خودِ نمونهی پیمایشگر را برگرداند (نه یک نمونهی پیمایشگر جدید).
یک اشارهگر اختیاری به آرایهی ایستای مختوم به NULL از ساختارهای PyMemberDef، که اعضای دادهی عادی (فیلدها یا جایگاهها) از نمونههای این نوع را اعلان میکند.
برای هر ورودی در آرایه، ورودیای به دیکشنری نوع افزوده میشود (به tp_dict در ادامه مراجعه کنید) که حاوی یک توصیفگر عضو است.
ارثبری:
این فیلد به زیرنوعها به ارث نمیرسد (اعضا از طریق سازوکاری متفاوت به ارث میرسند).
اشارهگری اختیاری به یک نوع پایه که ویژگیهای نوع از آن به ارث میرسند. در این سطح، تنها ارثبری تکی پشتیبانی میشود؛ ارثبری چندگانه نیازمند ایجاد یک شیء نوع بهصورت پویا با فراخوانی فرانوع است.
مقداردهی اولیه جایگاه از قواعد مقداردهی اولیه متغیرهای سراسری پیروی میکند. C99 الزام میکند که مقداردهندههای اولیه «ثابتهای آدرس» باشند. نشانگرهای تابع (function designator) مانند PyType_GenericNew() که بهطور ضمنی به اشارهگر تبدیل میشوند، ثابتهای آدرس معتبر در C99 هستند.
با این حال، الزامی نیست که عملگر یکعملوندی '&' اعمالشده بر متغیری غیرایستا مانند PyBaseObject_Type ثابت آدرس (address constant) تولید کند. کامپایلرها ممکن است از این پشتیبانی کنند (gcc پشتیبانی میکند)، اما MSVC پشتیبانی نمیکند. هر دو کامپایلر در این رفتار خاص کاملاً منطبق با استاندارد هستند.
در نتیجه، tp_base باید در تابع init ماژول توسعهای تنظیم شود.
ارثبری:
این فیلد به زیرنوعها به ارث نمیرسد (بدیهی است).
پیشفرض:
مقدار پیشفرض این فیلد &PyBaseObject_Type است (که برای برنامهنویسان پایتون بهعنوان نوع object شناخته میشود).
این فیلد بهطور معمول باید پیش از فراخوانی PyType_Ready به NULL مقداردهی اولیه شود؛ همچنین میتواند به یک دیکشنری حاوی ویژگیهای اولیه برای نوع مقداردهی اولیه شود. پس از آنکه PyType_Ready() نوع را مقداردهی اولیه کرد، ویژگیهای اضافی برای نوع تنها در صورتی میتوانند به این دیکشنری افزوده شوند که با عملیاتهای سربارگذاریشده (مانند __add__()) تناظر نداشته باشند. پس از پایان مقداردهی اولیه نوع، این فیلد باید فقطخواندنی در نظر گرفته شود.
برخی نوعها ممکن است دیکشنری خود را در این جایگاه ذخیره نکنند. برای بازیابی دیکشنری یک نوع دلخواه، از PyType_GetDict() استفاده کنید.
تغییر یافته در نسخهی 3.12: جزئیات داخلی: برای نوعهای توکار ایستا، این همیشه NULL است. در عوض، دیکشنری چنین نوعهایی در PyInterpreterState ذخیره میشود. برای دریافت دیکشنری یک نوع دلخواه از PyType_GetDict() استفاده کنید.
ارثبری:
این فیلد توسط زیرنوعها به ارث برده نمیشود (هرچند ویژگیهای تعریفشده در آن از طریق سازوکاری متفاوت به ارث برده میشوند).
پیشفرض:
اگر این فیلد NULL باشد، PyType_Ready() یک دیکشنری جدید به آن انتساب میدهد.
هشدار
استفاده از PyDict_SetItem() بر روی tp_dict یا تغییر دادن آن به روشهای دیگر با C-API دیکشنری ایمن نیست.
با اینکه این فیلد هنوز پشتیبانی میشود، در صورت امکان باید به جای آن از Py_TPFLAGS_MANAGED_DICT استفاده شود.
اگر نمونههای این نوع دیکشنری حاوی متغیرهای نمونه داشته باشند، این فیلد ناصفر است و آفست دیکشنری متغیرهای نمونه در نمونههای نوع را در بر میگیرد؛ این آفست توسط PyObject_GenericGetAttr() استفاده میشود.
این فیلد را با tp_dict اشتباه نگیرید؛ آن، دیکشنریِ مربوط به ویژگیهای خودِ شیء نوع است.
این مقدار، آفست دیکشنری را از ابتدای ساختار نمونه مشخص میکند.
tp_dictoffset باید فقطنوشتنی در نظر گرفته شود. برای بهدستآوردن اشارهگر به دیکشنری، PyObject_GenericGetDict() را فراخوانی کنید. فراخوانی PyObject_GenericGetDict() ممکن است نیاز به تخصیص حافظه برای دیکشنری داشته باشد، بنابراین ممکن است هنگام دسترسی به یک ویژگی روی شیء، فراخوانی PyObject_GetAttr() کارآمدتر باشد.
این فیلد توسط زیرنوعها به ارث برده میشود. زیرنوع نباید این آفست را بازنویسی کند؛ انجام این کار میتواند ناامن باشد، اگر کد C تلاش کند به دیکشنری در آفست قبلی دسترسی پیدا کند. برای پشتیبانی صحیح از ارثبری، از Py_TPFLAGS_MANAGED_DICT استفاده کنید.
پیشفرض:
این جایگاه مقدار پیشفرضی ندارد. برای نوعهای ایستا، اگر فیلد NULL باشد، برای نمونهها هیچ __dict__ ایجاد نمیشود.
این تابع معادل متد __init__() کلاسها است. مانند __init__()، میتوان نمونهای را بدون فراخوانی __init__() ایجاد کرد، و میتوان نمونهای را با فراخوانی دوباره متد __init__() آن مقداردهی مجدد کرد.
آرگومان self نمونهای است که باید مقداردهی اولیه شود؛ آرگومانهای args و kwds آرگومانهای جایگاهی و کلیدواژهای فراخوانی __init__() را بازنمایی میکنند.
تابع tp_init، در صورتی که NULL نباشد، هنگامی فراخوانی میشود که نمونهای بهطور معمول با فراخوانی نوع آن ایجاد شود، پس از آنکه تابع tp_new آن نوع، نمونهای از آن نوع را برگردانده باشد. اگر تابع tp_new نمونهای از نوع دیگری را برگرداند که زیرنوع نوع اصلی نیست، هیچ تابع tp_init فراخوانی نمیشود؛ اگر tp_new نمونهای از یک زیرنوع از نوع اصلی را برگرداند، tp_init آن زیرنوع فراخوانی میشود.
در صورت موفقیت 0 و در صورت خطا -1 را برمیگرداند و استثنایی تنظیم میکند.
آرگومان subtype نوع شیءای است که در حال ایجاد شدن است؛ آرگومانهای args و kwds آرگومانهای جایگاهی و کلیدواژهای فراخوانی آن نوع را نشان میدهند. توجه داشته باشید که subtype لازم نیست با نوعی که تابع tp_new آن فراخوانی میشود برابر باشد؛ میتواند زیرنوعی از آن نوع باشد (اما نه نوعی نامرتبط).
تابع tp_new باید برای تخصیص فضای شیء، subtype->tp_alloc(subtype,nitems) را فراخوانی کند و سپس تنها به اندازهای که کاملاً ضروری است، مقداردهی اولیهی بیشتری انجام دهد. مقداردهی اولیهای که میتوان آن را با اطمینان نادیده گرفت یا تکرار کرد، باید در هندلر tp_init قرار گیرد. یک قاعده سرانگشتی خوب این است که برای نوعهای تغییرناپذیر، تمام مقداردهی اولیه باید در tp_new انجام شود، در حالی که برای نوعهای تغییرپذیر، بیشترِ مقداردهی اولیه باید به tp_init موکول شود.
این فیلد توسط زیرنوعها به ارث برده میشود، بهجز اینکه نوعهای ایستا که tp_base آنها NULL یا &PyBaseObject_Type است، این فیلد را به ارث نمیبرند.
پیشفرض:
برای نوعهای ایستا این فیلد مقدار پیشفرضی ندارد. این بدان معناست که اگر جایگاه بهصورت NULL تعریف شود، نمیتوان نوع را برای ایجاد نمونههای جدید فراخوانی کرد؛ احتمالاً راه دیگری برای ایجاد نمونهها وجود دارد، مانند یک تابع کارخانهای.
اشارهگری اختیاری به تابع تخصیصگشایی نمونه. امضای آن چنین است:
voidtp_free(void*self);
این تابع باید حافظهی تخصیصیافته توسط tp_alloc را آزاد کند.
ارثبری:
زیرنوعهای ایستا این جایگاه را به ارث میبرند؛ این جایگاه در صورتی که از object به ارث برده شود، PyObject_Free() خواهد بود. استثنا: اگر نوع از زبالهروبی پشتیبانی کند (یعنی پرچم Py_TPFLAGS_HAVE_GC در tp_flags تنظیم شده باشد) و قرار باشد PyObject_Free() را به ارث ببرد، آنگاه این جایگاه به ارث برده نمیشود، بلکه پیشفرض آن PyObject_GC_Del() است.
اشارهگر اختیاری به تابعی که توسط زبالهروب فراخوانی میشود.
زبالهروب نیاز دارد بداند که آیا یک شیء خاص قابل جمعآوری است یا خیر. در حالت عادی، نگاه کردن به فیلد tp_flags نوع شیء و بررسی بیت پرچم Py_TPFLAGS_HAVE_GC کافی است. اما برخی نوعها ترکیبی از نمونههای تخصیصیافته بهصورت ایستا و پویا دارند و نمونههای تخصیصیافته بهصورت ایستا قابل جمعآوری نیستند. چنین نوعهایی باید این تابع را تعریف کنند؛ این تابع باید برای یک نمونهی قابل جمعآوری 1 و برای یک نمونهی غیرقابل جمعآوری 0 بازگرداند. امضا به این صورت است:
inttp_is_gc(PyObject*self);
(تنها نمونه از این مورد، خودِ نوعها هستند. فراکلاس، PyType_Type، این تابع را برای تمایز بین نوعهای تخصیصیافته بهصورت ایستا و نوعهای تخصیصیافته بهصورت پویا تعریف میکند.)
ارثبری:
این فیلد به زیرنوعها به ارث میرسد.
پیشفرض:
این جایگاه مقدار پیشفرض ندارد. اگر این فیلد NULL باشد، Py_TPFLAGS_HAVE_GC بهعنوان معادل عملکردی استفاده میشود.
این فیلد باید روی NULL تنظیم شود و بهعنوان فقطخواندنی در نظر گرفته شود. پایتون آن را زمانی که نوع مقداردهیاولیه شود، پر میکند.
برای شناسهی جایگاه متناظر، Py_tp_bases را ببینید.
هشدار
وراثت چندگانه برای نوعهای تعریفشده بهصورت ایستا بهخوبی کار نمیکند. اگر tp_bases را روی یک تاپل تنظیم کنید، پایتون خطایی ایجاد نمیکند، اما برخی جایگاهها فقط از اولین پایه به ارث برده میشوند.
سر فهرست ارجاعهای ضعیف، برای ارجاعهای ضعیف به این شیء نوع. به ارث برده نمیشود. فقط برای استفادهی داخلی.
تغییر یافته در نسخهی 3.12: جزئیات داخلی: برای نوعهای توکار ایستا، این همیشه NULL است، حتی اگر ارجاعهای ضعیف اضافه شوند. در عوض، ارجاعهای ضعیف هر یک روی PyInterpreterState ذخیره میشوند. برای پرهیز از این تمایز، از C-API عمومی یا ماکرو داخلی _PyObject_GET_WEAKREFS_LISTPTR() استفاده کنید.
The corresponding slot IDPy_tp_finalize is part of the ABI پایدار از نسخهی 3.5.
یک اشارهگر اختیاری به تابع نهاییسازی نمونه. این، پیادهسازی C متد ویژهی __del__() است. امضای آن به این صورت است:
voidtp_finalize(PyObject*self);
هدف اصلی نهاییسازی، انجام هر پاکسازی غیربدیهی است که باید پیش از نابود شدن شیء انجام شود، در حالی که شیء و هر شیء دیگری که مستقیماً یا غیرمستقیم به آنها ارجاع میدهد، همچنان در وضعیت سازگار هستند. نهاییساز مجاز است کد دلخواه پایتون را اجرا کند.
پیش از آنکه پایتون یک شیء را بهطور خودکار نهاییسازی کند، ممکن است برخی از ارجاعشوندههای (referents) مستقیم یا غیرمستقیم آن شیء، خودشان بهطور خودکار نهاییسازی شده باشند. با این حال، هیچیک از ارجاعشوندهها هنوز بهطور خودکار پاکسازی (tp_clear) نشدهاند.
ممکن است سایر اشیای نهایینشده همچنان در حال استفاده از یک شیء نهاییشده باشند، بنابراین نهاییساز باید شیء را در وضعیتی سالم باقی بگذارد (مثلاً، ناورداها همچنان برقرار باشند).
نکته
پس از آنکه پایتون بهطور خودکار یک شیء را نهاییسازی میکند، ممکن است پایتون پاکسازی خودکار (tp_clear) آن شیء و ارجاعشدههایش (مستقیم و غیرمستقیم) را آغاز کند. تضمینی نیست که اشیای پاکسازیشده در وضعیتی سازگار باشند؛ یک شیء نهاییسازیشده باید بتواند ارجاعشدههای پاکسازیشده را تحمل کند.
نکته
تضمین نمیشود که یک شیء پیش از فراخوانی مخرب آن (tp_dealloc) بهطور خودکار نهاییسازی شده باشد. توصیه میشود PyObject_CallFinalizerFromDealloc() را در ابتدای tp_dealloc فراخوانی کنید تا تضمین شود که شیء همیشه پیش از تخریب نهاییسازی میشود.
تابع tp_finalize میتواند در طول خاموشی، پس از حذف برخی از متغیرهای سراسری فراخوانی شود. برای جزئیات، مستندات متد __del__() را ببینید.
وقتی پایتون یک شیء را نهاییسازی میکند، مانند الگوریتم زیر رفتار میکند:
پایتون ممکن است شیء را بهعنوان نهاییشده علامتگذاری کند. در حال حاضر، پایتون همیشه اشیایی را که نوع آنها از زبالهروبی پشتیبانی میکند (یعنی پرچم Py_TPFLAGS_HAVE_GC در tp_flags تنظیم شده است) علامتگذاری میکند و هرگز اشیایی از انواع دیگر را علامتگذاری نمیکند؛ این ممکن است در نسخهای در آینده تغییر کند.
اگر شیء بهعنوان نهاییسازیشده نشانهگذاری نشده باشد و تابع نهاییساز tp_finalize آن غیر NULL باشد، تابع نهاییساز فراخوانی میشود.
اگر تابع نهاییساز فراخوانی شده باشد و نهاییساز شیء را دسترسپذیر کرده باشد (یعنی ارجاعی به شیء وجود داشته باشد و شیء عضوی از cyclic isolate نباشد)، آنگاه گفته میشود که نهاییساز شیء را احیا کرده است. مشخص نشده است که آیا نهاییساز میتواند با افزودن ارجاع جدیدی به شیء که آن را دسترسپذیر نمیکند، شیء را نیز احیا کند، یعنی شیء (هنوز) عضوی از یک ایزوله چرخهای است.
اگر نهاییساز شیء را احیا کرده باشد، نابودی در انتظار شیء لغو میشود و نشانهی نهاییشده شیء در صورت وجود ممکن است حذف شود. در حال حاضر، پایتون هرگز نشانهی نهاییشده را حذف نمیکند؛ این ممکن است در نسخههای آینده تغییر کند.
نهاییسازی خودکار به هر نهاییسازیای اشاره دارد که توسط پایتون انجام میشود، بهجز از طریق فراخوانی PyObject_CallFinalizer() یا PyObject_CallFinalizerFromDealloc(). هیچ تضمینی ارائه نمیشود که یک شیء چه زمانی، آیا اصلاً، یا هر چند وقت یکبار بهطور خودکار نهاییسازی میشود، مگر:
پایتون در صورتی که شیءای در دسترس باشد — یعنی ارجاعی به آن وجود داشته باشد و عضوی از یک cyclic isolate نباشد — آن را بهطور خودکار نهاییسازی نمیکند.
پایتون بهطور خودکار یک شیء را نهاییسازی نمیکند اگر نهاییسازی آن، آن شیء را بهعنوان نهاییشده علامتگذاری نکند. در حال حاضر، این مورد بر اشیائی اعمال میشود که نوع آنها از زبالهروبی پشتیبانی نمیکند؛ یعنی پرچم Py_TPFLAGS_HAVE_GC تنظیم نشده است. اینگونه اشیاء همچنان میتوانند با فراخوانی PyObject_CallFinalizer() یا PyObject_CallFinalizerFromDealloc() بهصورت دستی نهاییسازی شوند.
پایتون بهطور خودکار هیچ دو عضو از یک cyclic isolate را بهطور همزمان نهاییسازی نمیکند.
پایتون پس از پاکسازی خودکار (tp_clear) یک شیء، آن شیء را بهطور خودکار نهایی نمیکند.
اگر یک شیء عضوی از یک مجموعه چرخهای مجزا باشد، پایتون پس از پاکسازی خودکار هر عضو دیگر (به tp_clear مراجعه کنید)، آن را بهطور خودکار نهاییسازی نخواهد کرد.
پایتون پیش از آنکه بهطور خودکار هر یک از آنها را پاکسازی کند (رجوع کنید به tp_clear)، بهطور خودکار هر عضو از یک cyclic isolate را نهایی میکند.
اگر پایتون بخواهد یک شیء را بهطور خودکار پاکسازی کند (tp_clear)، ابتدا شیء را بهطور خودکار نهاییسازی خواهد کرد.
پایتون در حال حاضر تنها اشیایی را بهطور خودکار نهاییسازی میکند که عضوی از یک cyclic isolate باشند، اما نسخههای آینده ممکن است اشیاء را بهطور منظم پیش از تخریبشان نهاییسازی کنند.
staticvoidfoo_finalize(PyObject*self){// Save the current exception, if any.PyObject*exc=PyErr_GetRaisedException();// ...if(do_something_that_might_raise()!=success_indicator){PyErr_WriteUnraisable(self);gotodone;}done:// Restore the saved exception. This silently discards any exception// raised above, so be sure to call PyErr_WriteUnraisable first if// necessary.PyErr_SetRaisedException(exc);}
ارثبری:
این فیلد به زیرنوعها به ارث میرسد.
اضافه شده در نسخهی 3.4.
تغییر یافته در نسخهی 3.8: پیش از نسخه 3.8 برای استفاده از این فیلد، لازم بود که بیت پرچم Py_TPFLAGS_HAVE_FINALIZE تنظیم شود. این کار دیگر لازم نیست.
The corresponding slot IDPy_tp_vectorcall is part of the ABI پایدار از نسخهی 3.14.
یک تابع vectorcall برای استفاده در فراخوانیهای این شیء نوع (به جای نمونهها). به عبارت دیگر، tp_vectorcall میتواند برای بهینهسازی type.__call__ استفاده شود که بهطور معمول نمونهی جدیدی از نوع را برمیگرداند.
مانند هر تابع vectorcall، اگر tp_vectorcall برابر NULL باشد، بهجای آن از پروتکل tp_call (Py_TYPE(type)->tp_call) استفاده میشود.
نکته
پروتکل vectorcall ایجاب میکند که تابع vectorcall همان رفتار tp_call متناظر را داشته باشد. این بدان معناست که type->tp_vectorcall باید با رفتار Py_TYPE(type)->tp_call مطابقت داشته باشد.
بهطور خاص، اگر نوع از فراکلاس پیشفرض استفاده کند، type->tp_vectorcall باید همانند PyType_Type->tp_call رفتار کند، که:
type->tp_new را فرامیخواند،
اگر نتیجه زیرکلاسی از type باشد، type->tp_init را روی نتیجهی tp_new فراخوانی میکند و
نتیجهی tp_new را برمیگرداند.
معمولاً tp_vectorcall بازنویسی میشود تا این فرایند برای tp_new و tp_init خاص بهینهسازی شود. هنگام انجام این کار برای نوعهایی که کاربران میتوانند از آنها زیرکلاس بسازند، توجه داشته باشید که هر دوی آنها میتوانند بازنویسی شوند (با استفاده از __new__() و __init__()، بهترتیب).
ارثبری:
این فیلد هرگز به ارث برده نمیشود.
اضافه شده در نسخهی 3.9: (این فیلد از نسخه 3.8 وجود دارد، اما تنها از نسخه 3.9 استفاده میشود)
بهطور سنتی، نوعهای تعریفشده در کد C ایستا هستند؛ یعنی یک ساختار ایستای PyTypeObject مستقیماً در کد تعریف میشود و با استفاده از PyType_Ready() مقداردهی اولیه میشود.
این امر منجر به نوعهایی میشود که نسبت به نوعهای تعریفشده در پایتون محدود هستند:
نوعهای ایستا به یک کلاس پایه محدود هستند، یعنی نمیتوانند از وراثت چندگانه استفاده کنند.
شیءهای نوع ایستا (اما نه لزوماً نمونههای آنها) تغییرناپذیر هستند. افزودن یا تغییر دادن ویژگیهای شیء نوع از پایتون امکانپذیر نیست.
اشیاء نوع ایستا بین زیرمفسرها به اشتراک گذاشته میشوند، بنابراین نباید شامل هیچ وضعیت مختص زیرمفسر باشند.
همچنین، از آنجا که PyTypeObject تنها بهعنوان یک ساختار مات بخشی از API محدود است، هر ماژول توسعهای که از نوعهای ایستا استفاده میکند، باید برای یک نسخهی فرعی مشخص از پایتون کامپایل شود.
جایگزینی برای نوعهای ایستا، نوعهای تخصیصیافته در هیپ یا بهاختصار نوعهای هیپ است که تطابق نزدیکی با کلاسهای ایجادشده توسط دستور class پایتون دارند. پرچم Py_TPFLAGS_HEAPTYPE در نوعهای هیپ تنظیمشده است.
این ساختار اشارهگرهایی به توابعی را در خود نگه میدارد که یک شیء برای پیادهسازی پروتکل عددی از آنها استفاده میکند. هر تابع توسط تابعی با نام مشابه که در بخش پروتکل عدد مستند شده است، استفاده میشود.
توابع دوعملوندی و سهعملوندی باید نوع همهی عملوندهای خود را بررسی کنند و تبدیلهای لازم را پیادهسازی کنند (حداقل یکی از عملوندها نمونهای از نوع تعریفشده است). اگر عملیات برای عملوندهای دادهشده تعریفنشده باشد، توابع دوعملوندی و سهعملوندی باید Py_NotImplemented را برگردانند و اگر خطای دیگری رخ داده باشد، باید NULL را برگردانند و یک استثنا تنظیم کنند.
نکته
فیلد nb_reserved باید همیشه NULL باشد. این فیلد پیشتر nb_long نامیده میشد و در پایتون 3.0.1 تغییر نام یافت.
این تابع توسط PyMapping_Size() و PyObject_Size() استفاده میشود و همان امضا را دارد. اگر شیء طول تعریفشدهای نداشته باشد، این جایگاه میتواند به NULL تنظیم شود.
این تابع توسط PySequence_Concat() استفاده میشود و امضای یکسانی دارد. همچنین پس از تلاش برای انجام جمع عددی از طریق جایگاه nb_add، توسط عملگر + نیز استفاده میشود.
این تابع توسط PySequence_Repeat() استفاده میشود و امضای یکسانی دارد. عملگر * نیز پس از تلاش برای ضرب عددی از طریق جایگاه nb_multiply از این تابع استفاده میکند.
این تابع توسط PySequence_GetItem() استفاده میشود و امضای یکسانی دارد. این تابع همچنین توسط PyObject_GetItem() استفاده میشود، پس از تلاش برای زیرنویسی از طریق جایگاه mp_subscript. این جایگاه باید پرشده باشد تا تابع PySequence_Check() مقدار 1 را برگرداند؛ در غیر این صورت میتواند NULL باشد.
اندیسهای منفی به صورت زیر مدیریت میشوند: اگر جایگاه sq_length پر شده باشد، فراخوانی میشود و از طول دنباله برای محاسبهی اندیس مثبتی که به sq_item ارسال میشود، استفاده میشود. اگر sq_length برابر NULL باشد، اندیس بدون تغییر به تابع ارسال میشود.
این تابع توسط PySequence_SetItem() استفاده میشود و همان امضا را دارد. این تابع همچنین پس از تلاش برای انجام انتساب و حذف آیتم از طریق جایگاه mp_ass_subscript، توسط PyObject_SetItem() و PyObject_DelItem() استفاده میشود. اگر شیء از انتساب و حذف آیتم پشتیبانی نکند، میتوان این جایگاه را NULL گذاشت.
این تابع ممکن است توسط PySequence_Contains() استفاده شود و همان امضا را دارد. میتوان این جایگاه را NULL گذاشت؛ در این صورت PySequence_Contains() صرفاً دنباله را پیمایش میکند تا زمانی که مورد منطبقی بیابد.
The corresponding slot IDPy_sq_inplace_concat is part of the ABI پایدار.
این تابع توسط PySequence_InPlaceConcat() استفاده میشود و امضای یکسانی با آن دارد. این تابع باید عملوند اول خود را تغییر دهد و آن را برگرداند. این جایگاه میتواند NULL باقی بماند؛ در این صورت PySequence_InPlaceConcat() به PySequence_Concat() بازمیگردد. این جایگاه همچنین توسط انتساب افزودهی += استفاده میشود، پس از تلاش برای انجام جمع درجای عددی از طریق جایگاه nb_inplace_add.
The corresponding slot IDPy_sq_inplace_repeat is part of the ABI پایدار.
این تابع توسط PySequence_InPlaceRepeat() استفاده میشود و امضای یکسانی دارد. باید عملوند اول خود را تغییر دهد و آن را بازگرداند. این جایگاه را میتوان NULL گذاشت؛ در این صورت PySequence_InPlaceRepeat() به PySequence_Repeat() بازمیگردد. این جایگاه همچنین توسط عملگر انتساب افزوده *= استفاده میشود، پس از تلاش برای ضرب عددی درجا از طریق جایگاه nb_inplace_multiply.
این ساختار اشارهگرهایی به توابع مورد نیاز پروتکل بافر را نگه میدارد. این پروتکل تعریف میکند که یک شیء اکسپورتکننده چگونه میتواند دادههای داخلی خود را در دسترس اشیاء مصرفکننده قرار دهد.
The corresponding slot IDPy_bf_getbuffer is part of the ABI پایدار از نسخهی 3.11.
امضای این تابع به این صورت است:
int(PyObject*exporter,Py_buffer*view,intflags);
درخواستی به exporter برای پر کردن view مطابق با flags را مدیریت میکند. بهجز مورد (۳)، پیادهسازی این تابع باید این گامها را بردارد:
بررسی کنید که آیا درخواست قابل برآوردن است یا خیر. در غیر این صورت، استثنای BufferError را ایجاد کنید، view->obj را برابر NULL قرار دهید و -1 را برگردانید.
فیلدهای درخواستی را پر کنید.
شمارندهی داخلی تعداد اکسپورت را افزایش میدهد.
مقدار view->obj را برابر exporter قرار دهید و view->obj را افزایش دهید.
افزایش شمارندهی اکسپورت (export) در گام (۳) اتمیک است.
دادههای بافر زیرین برای طول عمر همهی اکسپورتها (export) معتبر و در مکان حافظهای پایدار باقی میمانند.
برای اشیایی که از تغییر اندازه یا تخصیص مجدد پشتیبانی میکنند (مانند bytearray)، شمارنده اکسپورت پیش از انجام چنین عملیاتهایی بهصورت اتمیک بررسی میشود و در صورت وجود اکسپورتها، BufferError ایجاد میشود.
اگر اکسپورتکننده (exporter) بخشی از زنجیره یا درختی از تأمینکنندگان بافر باشد، میتوان از دو طرحوارهی اصلی استفاده کرد:
اکسپورت کردن مجدد: هر عضو درخت بهعنوان شیء اکسپورتکننده عمل میکند و view->obj را برابر یک ارجاع جدید به خود قرار میدهد.
هدایت مجدد: درخواست بافر به شیء ریشهی درخت هدایت مجدد میشود. در اینجا، view->obj ارجاعی جدید به شیء ریشه خواهد بود.
فیلدهای جداگانهی view در بخش ساختار بافر توصیف شدهاند و قواعدی که اکسپورتکننده باید بر اساس آنها به درخواستهای خاص واکنش نشان دهد، در بخش انواع درخواست بافر آمده است.
تمام حافظهای که در ساختار Py_buffer به آن اشاره میشود متعلق به اکسپورتکننده است و باید تا زمانی که هیچ مصرفکنندهای باقی نمانده باشد معتبر بماند. format، shape، strides، suboffsets و internal برای مصرفکننده فقطخواندنی هستند.
PyBuffer_FillInfo() راهی آسان برای در معرض قرار دادن یک بافر بایت ساده فراهم میکند، در حالی که با همهی انواع درخواست بهدرستی برخورد میکند.
The corresponding slot IDPy_bf_releasebuffer is part of the ABI پایدار از نسخهی 3.11.
امضای این تابع به این صورت است:
void(PyObject*exporter,Py_buffer*view);
درخواست آزاد کردن منابع بافر را مدیریت میکند. اگر هیچ منبعی نیاز به آزاد شدن نداشته باشد، PyBufferProcs.bf_releasebuffer میتواند NULL باشد. در غیر این صورت، یک پیادهسازی استاندارد از این تابع گامهای اختیاری زیر را برمیدارد:
شمارنده داخلی تعداد اکسپورتهارا را کاهش دهید.
اگر شمارنده 0 باشد، تمام حافظهی مرتبط با view را آزاد کنید.
کاهش شمارندهی اکسپورت در گام (۱) باید اتمیک باشد.
پاکسازی منابع هنگامی که شمارنده به صفر میرسد باید بهصورت اتمیک انجام شود، زیرا آزادسازی نهایی ممکن است با آزادسازیهای همزمان از سوی نخهای دیگر رقابت کند و تخصیصزدایی باید تنها یکبار رخ دهد.
اکسپورتکننده باید از فیلد internal برای پیگیری منابع مخصوص بافر استفاده کند. تضمین میشود که این فیلد ثابت باقی بماند، در حالی که مصرفکننده میتواند رونوشتی از بافر اصلی را بهعنوان آرگومان view ارسال کند.
این تابع به هیچ وجه نباید view->obj را کاهش دهد، زیرا این کار بهطور خودکار در PyBuffer_Release() انجام میشود (این طرح برای شکستن چرخههای ارجاع مفید است).
PyBuffer_Release() رابطی برای مصرفکننده است که این تابع را پوشش میدهد.
هدف این تابع، جداسازی تخصیص حافظه از مقداردهی اولیه حافظه است. این تابع باید اشارهگری به بلوکی از حافظه با طول مناسب برای نمونه، همترازشده بهدرستی و مقداردهیشده به صفر برگرداند، اما بهطوریکه ob_refcnt روی 1 و ob_type روی آرگومان نوع تنظیم شده باشد. اگر tp_itemsize نوع غیرصفر باشد، فیلد ob_size شیء باید به nitems مقداردهی اولیه شود و طول بلوک حافظه تخصیصیافته باید tp_basicsize+nitems*tp_itemsize باشد که به سمت بالا به مضربی از sizeof(void*) گرد شده است؛ در غیر این صورت، nitems استفاده نمیشود و طول بلوک باید tp_basicsize باشد.
این تابع نباید هیچ مقداردهی اولیهی دیگری روی نمونه انجام دهد، حتی تخصیص حافظهی اضافی؛ این کار باید توسط tp_new انجام شود.
در ادامه، نمونههای سادهای از تعریف نوعها در پایتون آمده است. این نمونهها شامل کاربردهای رایجی هستند که ممکن است با آنها مواجه شوید. برخی از آنها موارد گوشهای پیچیده را نشان میدهند. برای مثالهای بیشتر، اطلاعات کاربردی و یک آموزش، به تعریف نوعهای توسعهای: آموزش و تعریف نوعهای توسعهای: موضوعات گوناگون مراجعه کنید.
زیرکلاسی از str که نمیتوان از آن زیرکلاس گرفت و نمیتوان آن را برای ایجاد نمونه فراخوانی کرد (مثلاً از یک تابع کارخانهای جداگانه استفاده میکند) با استفاده از پرچم Py_TPFLAGS_DISALLOW_INSTANTIATION:
typedefstruct{PyUnicodeObjectraw;char*extra;}MyStr;staticPyTypeObjectMyStr_Type={PyVarObject_HEAD_INIT(NULL,0).tp_name="mymod.MyStr",.tp_basicsize=sizeof(MyStr),.tp_base=NULL,// set to &PyUnicode_Type in module init.tp_doc=PyDoc_STR("my custom str"),.tp_flags=Py_TPFLAGS_DEFAULT|Py_TPFLAGS_DISALLOW_INSTANTIATION,.tp_repr=(reprfunc)myobj_repr,};
آخرین بهروزرسانی در نوامبر 12, 2026 (06:19 UTC).
آیا اشکالی یافتید؟
ساخته شده با Sphinx 9.1.0.
You are visitor 01402503 — 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.