چرخهی حیات شیء¶
این بخش توضیح میدهد که جایگاههای یک نوع در طول عمر یک شیء چگونه با یکدیگر در ارتباط هستند. این بخش بهعنوان مرجع کامل و کانونیکال برای جایگاهها در نظر گرفته نشده است؛ در عوض، برای جزئیات دربارهی یک جایگاه خاص، به مستندات اختصاصی جایگاه در ساختارهای شیء نوع مراجعه کنید.
رویدادهای چرخه حیات¶
شکل زیر ترتیب رویدادهایی را که در طول عمر یک شیء میتوانند رخ دهند نشان میدهد. پیکانی از A به B نشان میدهد که رویداد B میتواند پس از رخ دادن رویداد A رخ دهد، و برچسب پیکان، شرطی را که برای رخ دادن B پس از A باید برقرار باشد، مشخص میکند.
توضیح:
هنگامی که شیء جدیدی با فراخوانی نوع آن ساخته میشود:
tp_newبرای ایجاد یک شیء جدید فراخوانی میشود.tp_allocمستقیماً توسطtp_newبرای تخصیص حافظهی شیء جدید فراخوانی میشود.tp_initشیء تازهایجادشده را مقداردهی اولیه میکند. در صورت تمایل میتوانtp_initرا دوباره فراخوانی کرد تا یک شیء مجدداً مقداردهی اولیه شود. همچنین میتوان از فراخوانیtp_initبهطور کامل صرفنظر کرد، برای مثال توسط کد پایتونی که__new__()را فراخوانی میکند.
پس از تکمیل
tp_init، شیء آماده استفاده است.مدتی پس از حذف آخرین ارجاع به یک شیء:
اگر شیئی بهعنوان نهاییشده علامتگذاری نشده باشد، میتوان آن را با علامتگذاری بهعنوان نهاییشده و فراخوانی تابع
tp_finalizeآن نهاییسازی کرد. پایتون هنگامی که آخرین ارجاع به یک شیء حذف میشود، آن را نهاییسازی نمیکند؛ برای اطمینان از اینکهtp_finalizeهمیشه فراخوانی میشود، ازPyObject_CallFinalizerFromDealloc()استفاده کنید.اگر شیء بهعنوان نهاییسازیشده علامتگذاری شده باشد، ممکن است
tp_clearتوسط زبالهروب فراخوانی شود تا ارجاعهایی را که شیء نگهداشته است پاک کند. این متد هنگامی که شمارش ارجاع شیء به صفر برسد فراخوانی نمیشود.tp_deallocبرای نابود کردن شیء فراخوانی میشود. برای جلوگیری از تکرار کد،tp_deallocمعمولاًtp_clearرا فراخوانی میکند تا ارجاعهای شیء را آزاد کند.وقتی
tp_deallocتخریب شیء را به پایان میرساند، برای آزادسازی حافظهtp_freeرا مستقیماً فراخوانی میکند (که معمولاً بهطور خودکار و متناسب با نوع، رویPyObject_Free()یاPyObject_GC_Del()تنظیم میشود).
تابع
tp_finalizeدر صورت تمایل مجاز است ارجاعی به شیء اضافه کند. اگر چنین کند، شیء احیا میشود و از تخریب در انتظارش جلوگیری میشود. (تنهاtp_finalizeمجاز است یک شیء را احیا کند؛tp_clearوtp_deallocبدون فراخوانیtp_finalizeنمیتوانند این کار را انجام دهند.) احیای یک شیء ممکن است باعث حذف نشان نهاییشده از روی آن شود یا نشود. در حال حاضر، پایتون اگر شیء احیاشده از زبالهروبی پشتیبانی کند (یعنی پرچمPy_TPFLAGS_HAVE_GCتنظیم شده باشد)، نشان نهاییشده را از روی آن حذف نمیکند، اما اگر شیء از زبالهروبی پشتیبانی نکند، این نشان را حذف میکند؛ هر یک یا هر دوی این رفتارها ممکن است در آینده تغییر کنند.tp_deallocمیتواند در صورت تمایل به استفاده مجدد از آن کد برای کمک به تخریب شیء، بهصورت اختیاریtp_finalizeرا از طریقPyObject_CallFinalizerFromDealloc()فراخوانی کند. انجام این کار توصیه میشود، زیرا تضمین میکند کهtp_finalizeهمیشه پیش از تخریب فراخوانی میشود. برای مشاهده کد نمونه، به مستنداتtp_deallocمراجعه کنید.اگر شیء عضوی از یک cyclic isolate باشد و یا
tp_clearدر شکستن چرخه ارجاع ناکام بماند یا مجموعه ایزوله چرخهای تشخیص داده نشود (شایدgc.disable()فراخوانی شده باشد، یا پرچمPy_TPFLAGS_HAVE_GCبهاشتباه در یکی از نوعهای درگیر تنظیم نشده باشد)، اشیاء بهطور نامحدود غیرقابل جمعآوری باقی میمانند (آنها «نشت» میکنند).gc.garbageرا ببینید.
اگر شیء بهعنوان پشتیبان از زبالهروبی نشانهگذاری شده باشد (پرچم Py_TPFLAGS_HAVE_GC در tp_flags تنظیم شده باشد)، رویدادهای زیر نیز ممکناند:
زبالهروب گاهی برای شناسایی ایزولههای چرخهای،
tp_traverseرا فراخوانی میکند.هنگامی که زبالهروب یک cyclic isolate را کشف میکند، یکی از اشیاء درون گروه را با علامتگذاری آن بهعنوان نهاییشده و فراخوانی تابع
tp_finalizeآن (در صورت وجود) نهاییسازی میکند. این کار تا زمانی تکرار میشود که ایزوله چرخهای دیگر وجود نداشته باشد یا همهی اشیاء نهاییسازی شده باشند.tp_finalizeمجاز است با افزودن ارجاعی از بیرون cyclic isolate شیء را احیا کند. این ارجاع جدید باعث میشود که گروه اشیاء دیگر مجموعه ایزوله چرخهای تشکیل ندهد (ممکن است چرخه ارجاع همچنان وجود داشته باشد، اما در این صورت اشیاء دیگر ایزوله نیستند).وقتی زبالهروب یک مجموعهی ایزوله چرخهای را کشف میکند و همهی اشیاء در گروه از قبل بهعنوان نهاییسازیشده علامتگذاری شدهاند، زبالهروب یک یا چند شیء از اشیاء پاکنشده در گروه را (احتمالاً بهصورت همزمان) با فراخوانی تابع
tp_clearهر یک از آنها پاک میکند. این کار تا زمانی که مجموعهی ایزوله چرخهای همچنان وجود داشته باشد و همهی اشیاء هنوز پاک نشده باشند، تکرار میشود.
تخریب ایزولههای چرخهای¶
در ادامه، مراحل حیات یک مجموعهی ایزوله چرخهای فرضی که پس از نهایی شدن یا پاک شدن هر شیء عضو، همچنان وجود دارد، فهرست شدهاند. اگر یک مجموعهی ایزوله چرخهای از تمام این مراحل عبور کند، نشت حافظه رخ داده است؛ چنین مجموعهای باید به محض پاک شدن تمام اشیاء ناپدید شود، اگر نه زودتر از آن. مجموعهی ایزوله چرخهای میتواند یا به این دلیل ناپدید شود که چرخهی ارجاع شکسته شده است، یا به این دلیل که اشیاء در نتیجه احیا شدن توسط نهاییساز دیگر ایزوله نیستند (به tp_finalize مراجعه کنید).
قابل دسترس (هنوز ایزوله چرخهای نیست): همهی اشیاء در وضعیت عادی و قابل دسترس خود هستند. ممکن است چرخهی ارجاع وجود داشته باشد، اما وجود یک ارجاع بیرونی به این معناست که اشیاء هنوز ایزوله نشدهاند.
دسترسناپذیر اما سازگار: آخرین ارجاع از بیرون گروه چرخهای اشیاء حذف شده است و در نتیجه اشیاء ایزوله شدهاند (بدین ترتیب یک ایزوله چرخهای (cyclic isolate) متولد میشود). هیچیک از اشیاء گروه هنوز نهاییسازی یا پاکسازی نشدهاند. ایزوله چرخهای در این مرحله باقی میماند تا اجرای آیندهای از زبالهروب انجام شود (که لزوماً اجرای بعدی نیست، زیرا ممکن است اجرای بعدی هر شیء را پویش نکند).
ترکیبی از نهاییشده و نهایینشده: اشیاء در یک مجموعهی چرخهای ایزوله یکبهیک نهاییسازی میشوند، که این بدان معناست که برای مدتی، مجموعهی چرخهای ایزوله از ترکیبی از اشیاء نهاییشده و نهایینشده تشکیل شده است. ترتیب نهاییسازی نامشخص است، بنابراین ممکن است تصادفی به نظر برسد. یک شیء نهاییشده باید هنگامی که اشیاء نهایینشده با آن تعامل میکنند، به شیوهای منطقی رفتار کند، و یک شیء نهایینشده باید بتواند نهاییسازی زیرمجموعهی دلخواهی از اشیایی را که به آنها ارجاع دارد (referents) تحمل کند.
همه نهاییسازی میشوند: همهی اشیاء موجود در یک ایزوله چرخهای (cyclic isolate) پیش از پاک شدن هر یک از آنها نهاییسازی میشوند.
ترکیبی از نهاییسازیشده و پاکسازیشده: اشیاء میتوانند بهصورت متوالی یا همزمان پاکسازی شوند (اما با در اختیار داشتن قفل مفسر سراسری)؛ در هر دو حالت، برخی پیش از بقیه پایان مییابند. یک شیء نهاییسازیشده باید بتواند پاکسازی زیرمجموعهای از اشیایی که به آنها ارجاع دارد را تحمل کند. PEP 442 این مرحله را «زباله چرخهای» (cyclic trash) مینامد.
نشتشده: اگر یک ایزوله چرخهای پس از نهاییسازی و پاکسازی همهی اشیاء گروه همچنان وجود داشته باشد، در این صورت اشیاء برای همیشه غیرقابل جمعآوری باقی میمانند (به
gc.garbageمراجعه کنید). اگر یک ایزوله چرخهای به این مرحله برسد، این یک باگ است---یعنی متدهایtp_clearاشیاء شرکتکننده نتوانستهاند چرخهی ارجاع را همانطور که لازم است بشکنند.
اگر tp_clear وجود نداشت، پایتون هیچ راهی نداشت تا یک چرخهی ارجاع را بهطور ایمن قطع کند. صرفاً نابود کردن یک شیء در یک مجموعهی ایزوله چرخهای منجر به یک اشارهگر سرگردان میشد و هنگامی که شیئی که به شیء نابودشده ارجاع میکند، خود نابود شود، رفتار تعریفنشده را برمیانگیخت. مرحلهی پاکسازی، نابود کردن شیء را به فرایندی دو مرحلهای تبدیل میکند: نخست tp_clear فراخوانی میشود تا اشیاء را تا حدی نابود کند که از یکدیگر گرهگشایی شوند، سپس tp_dealloc فراخوانی میشود تا نابودی را کامل کند.
برخلاف پاکسازی، نهاییسازی مرحلهای از تخریب نیست. یک شیء نهاییشده همچنان باید با ادامهی برآوردن قراردادهای طراحیاش، بهدرستی رفتار کند. نهاییساز یک شیء مجاز است هر کد پایتونی را اجرا کند، و حتی مجاز است با افزودن یک ارجاع، از تخریب قریبالوقوع جلوگیری کند. نهاییساز تنها از نظر ترتیب فراخوانی با تخریب در ارتباط است---اگر اجرا شود، پیش از تخریب اجرا میشود؛ تخریبی که با tp_clear (در صورت فراخوانی) آغاز میشود و با tp_dealloc به پایان میرسد.
گام نهاییسازی برای بازپسگیری ایمن اشیاء در یک مجموعه چرخهای ایزوله (cyclic isolate) ضروری نیست، اما وجود آن طراحی نوعهایی را آسانتر میکند که هنگام پاکسازی اشیاء به شیوهای معقول رفتار کنند. پاکسازی یک شیء ممکن است آن را ناگزیر در وضعیتی معیوب و نیمهتخریبشده باقی بگذارد---ممکن است فراخوانی هر یک از متدهای شیء پاکسازیشده یا دسترسی به هر یک از ویژگیهای آن ناایمن باشد. با نهاییسازی، تنها اشیاء نهاییسازیشده ممکن است با اشیاء پاکسازیشده تعامل داشته باشند؛ برای اشیاء غیرنهاییسازیشده تضمین میشود که تنها با اشیاء پاکسازینشده (اما بهطور بالقوه نهاییسازیشده) تعامل داشته باشند.
برای جمعبندی تعاملهای ممکن:
یک شیء نهایینشده ممکن است ارجاعهایی به اشیاء نهایینشده و نهاییشده داشته باشد یا از آنها ارجاعی دریافت کند، اما به اشیاء پاکسازیشده ارجاعی ندارد و از آنها نیز ارجاعی دریافت نمیکند.
یک شیء نهاییشده ممکن است ارجاعهایی به اشیاء نهایینشده، نهاییشده و پاکسازیشده داشته باشد یا آن اشیاء ارجاعهایی به آن داشته باشند.
یک شیء پاکسازیشده ممکن است ارجاعهایی به اشیاء نهاییسازیشده و پاکسازیشده داشته باشد یا از آنها ارجاعی به آن وجود داشته باشد، اما هیچ ارجاعی به اشیاء نهاییسازینشده یا از آنها به آن وجود نخواهد داشت.
در نبود هرگونه چرخه ارجاع، یک شیء را میتوان بهسادگی به محض حذف آخرین ارجاع به آن نابود کرد؛ گامهای نهاییسازی و پاکسازی برای بازپسگیری ایمن اشیاء استفادهنشده ضروری نیستند. با این حال، میتواند مفید باشد که tp_finalize و tp_clear به هر جهت پیش از نابودی بهطور خودکار فراخوانی شوند، زیرا وقتی همه اشیاء همیشه دنباله یکسانی از رویدادها را تجربه کنند، صرفنظر از اینکه در یک مجموعه چرخهای ایزوله مشارکت داشتهاند یا نه، طراحی نوع سادهتر میشود. در حال حاضر، پایتون tp_finalize و tp_clear را تنها در صورت نیاز برای نابود کردن یک مجموعه چرخهای ایزوله فراخوانی میکند؛ این ممکن است در نسخهای آینده تغییر کند.
توابع¶
برای تخصیص و آزادسازی حافظه، به تخصیص اشیاء روی هیپ مراجعه کنید.
-
void PyObject_CallFinalizer(PyObject *op)¶
شیء را مطابق توضیحات دادهشده در
tp_finalizeنهاییسازی میکند. به جای فراخوانی مستقیمtp_finalize، این تابع (یاPyObject_CallFinalizerFromDealloc()) را فراخوانی کنید، زیرا این تابع ممکن است تکرار فراخوانیهای متعددtp_finalizeرا حذف کند. در حال حاضر، تکرار فراخوانیها تنها زمانی حذف میشود که نوع از زبالهروبی پشتیبانی کند (یعنی پرچمPy_TPFLAGS_HAVE_GCتنظیم شده باشد)؛ این موضوع ممکن است در آینده تغییر کند.اضافه شده در نسخهی 3.4.
-
int PyObject_CallFinalizerFromDealloc(PyObject *op)¶
همانند
PyObject_CallFinalizer()است، اما در نظر گرفته شده که در ابتدای مخرب شیء (tp_dealloc) فراخوانی شود. نباید هیچ ارجاعی به شیء وجود داشته باشد. اگر نهاییساز شیء آن را احیا کند، این تابع مقدار -1 را برمیگرداند؛ هیچ تخریب بیشتری نباید رخ دهد. در غیر این صورت، این تابع مقدار 0 را برمیگرداند و تخریب میتواند بهطور عادی ادامه یابد.اضافه شده در نسخهی 3.4.
همچنین ملاحظه نمائید
tp_deallocبرای کد نمونه.