weakref --- ارجاع‌های ضعیف

کد منبع: Lib/weakref.py


ماژول weakref به برنامه‌نویس پایتون اجازه می‌دهد تا ارجاع‌های ضعیف <weak references> به اشیاء ایجاد کند.

در ادامه، اصطلاح referent به معنای شیءای است که یک ارجاع ضعیف به آن اشاره می‌کند.

یک ارجاع ضعیف به یک شیء برای زنده نگه داشتن آن شیء کافی نیست: هنگامی که تنها ارجاع‌های باقی‌مانده به یک مورد ارجاع، ارجاع‌های ضعیف باشند، زباله‌روبی مختار است مورد ارجاع را نابود کند و حافظه‌ی آن را برای چیز دیگری بازاستفاده کند. با این حال، تا زمانی که شیء واقعاً نابود نشده باشد، ارجاع ضعیف ممکن است شیء را برگرداند، حتی اگر هیچ ارجاع قوی‌ای به آن وجود نداشته باشد.

یک کاربرد اصلی برای ارجاع‌های ضعیف، پیاده‌سازی نهانگاه‌ها یا نگاشت‌هایی است که اشیای بزرگ را نگه می‌دارند، در مواردی که مطلوب است یک شیء بزرگ صرفاً به این دلیل که در یک نهانگاه یا نگاشت قرار دارد، زنده نگه‌داشته نشود.

برای مثال، اگر تعدادی شیء تصویر دودویی بزرگ داشته باشید، ممکن است بخواهید نامی به هر یک اختصاص دهید. اگر از یک دیکشنری پایتون برای نگاشت نام‌ها به تصاویر، یا تصاویر به نام‌ها استفاده کرده باشید، اشیاء تصویر صرفاً به این دلیل که به‌عنوان مقدار یا کلید در دیکشنری‌ها حضور دارند، زنده می‌ماندند. کلاس‌های WeakKeyDictionary و WeakValueDictionary ارائه‌شده توسط ماژول weakref جایگزینی هستند که با استفاده از ارجاع‌های ضعیف (weak references)، نگاشت‌هایی می‌سازند که اشیاء را صرفاً به دلیل حضور در اشیاء نگاشت زنده نگه نمی‌دارند. برای مثال، اگر یک شیء تصویر یک مقدار در یک WeakValueDictionary باشد، آنگاه هنگامی که آخرین ارجاع‌های باقی‌مانده به آن شیء تصویر، همان ارجاع‌های ضعیف نگه‌داشته‌شده توسط نگاشت‌های ضعیف باشند، زباله‌روبی می‌تواند شیء را بازیابی کند و ورودی‌های متناظر آن در نگاشت‌های ضعیف به‌سادگی حذف می‌شوند.

WeakKeyDictionary و WeakValueDictionary در پیاده‌سازی خود از ارجاع‌های ضعیف استفاده می‌کنند و توابع کال‌بک را روی ارجاع‌های ضعیف تنظیم می‌کنند تا هنگامی که یک کلید یا مقدار توسط زباله‌روبی بازیابی می‌شود، به دیکشنری‌های ضعیف اطلاع دهند. WeakSet رابط set را پیاده‌سازی می‌کند، اما ارجاع‌های ضعیفی به عناصر خود نگه می‌دارد، درست همان‌گونه که WeakKeyDictionary چنین می‌کند.

finalize راه سرراستی برای ثبت یک تابع پاک‌سازی فراهم می‌کند تا هنگام زباله‌روبی یک شیء فراخوانی شود. استفاده از آن ساده‌تر از تنظیم یک تابع کال‌بک روی یک ارجاع ضعیف خام است، زیرا این ماژول به‌طور خودکار تضمین می‌کند که نهایی‌ساز (finalizer) تا زمان زباله‌روبی شدن شیء زنده می‌ماند.

انتظار می‌رود بیشتر برنامه‌ها متوجه شوند که استفاده از یکی از این انواع ظرف ضعیف یا finalize برایشان کافی است — معمولاً لازم نیست ارجاع‌های ضعیف خود را مستقیماً ایجاد کنید. ماژول weakref سازوکار سطح پایین را برای استفاده‌های پیشرفته در دسترس قرار می‌دهد.

همه‌ی اشیاء قابلیت ارجاع ضعیف ندارند. اشیایی که از ارجاع ضعیف پشتیبانی می‌کنند، شامل نمونه‌های کلاس، توابع نوشته‌شده به پایتون (اما نه به C)، متدهای نمونه، مجموعه‌ها، مجموعه‌های تغییرناپذیر (frozenset)، برخی اشیای پرونده، تولیدگرها، اشیای نوع، سوکت‌ها، آرایه‌ها، صف‌های دوطرفه (deque)، اشیای الگوی عبارت باقاعده و اشیای کد هستند.

تغییر یافته در نسخه‌ی 3.2: پشتیبانی از thread.lock، threading.Lock و اشیای کد اضافه شد.

چندین نوع توکار مانند list و dict به‌طور مستقیم از ارجاع‌های ضعیف پشتیبانی نمی‌کنند، اما می‌توان از طریق زیرکلاس‌سازی پشتیبانی را به آن‌ها افزود:

class Dict(dict):
    pass

obj = Dict(red=1, green=2, blue=3)   # this object is weak referenceable

سایر انواع توکار مانند tuple و int از ارجاع‌های ضعیف پشتیبانی نمی‌کنند، حتی در صورت زیرکلاس‌سازی.

انواع توسعه‌ای می‌توانند به‌سادگی برای پشتیبانی از ارجاع‌های ضعیف آماده شوند؛ پشتیبانی از ارجاع ضعیف را ببینید.

هنگامی که __slots__ برای یک نوع معین تعریف شده باشد، پشتیبانی از ارجاع ضعیف غیرفعال می‌شود، مگر آنکه یک رشته '__weakref__' نیز در دنباله‌ای از رشته‌های موجود در اعلان __slots__ وجود داشته باشد. برای جزئیات، مستندات __slots__ را ببینید.

class weakref.ref(object[, callback])

یک ارجاع ضعیف به object برمی‌گرداند. اگر موضوع ارجاع هنوز زنده باشد، می‌توان شیء اصلی را با فراخوانی شیء ارجاع بازیابی کرد؛ اگر موضوع ارجاع دیگر زنده نباشد، فراخوانی شیء ارجاع باعث می‌شود None برگردانده شود. اگر callback ارائه‌شده باشد و None نباشد، و شیء ارجاع ضعیف برگردانده‌شده هنوز زنده باشد، کال‌بک هنگامی فراخوانی خواهد شد که شیء در آستانه نهایی‌شدن باشد؛ شیء ارجاع ضعیف به‌عنوان تنها پارامتر به کال‌بک داده خواهد شد؛ موضوع ارجاع دیگر در دسترس نخواهد بود.

ایجاد چندین ارجاع ضعیف به یک شیء یکسان مجاز است. کال‌بک‌های ثبت‌شده برای هر ارجاع ضعیف، از جدیدترین کال‌بک ثبت‌شده تا قدیمی‌ترین کال‌بک ثبت‌شده فراخوانی می‌شوند.

استثناهای پرتاب‌شده توسط کال‌بک در خروجی خطای استاندارد ثبت خواهند شد، اما قابل انتشار نیستند؛ آن‌ها دقیقاً همانند استثناهای پرتاب‌شده از متد __del__() یک شیء مدیریت می‌شوند.

ارجاع‌های ضعیف در صورتی هش‌پذیر هستند که شیء هش‌پذیر باشد. آن‌ها مقدار هش خود را حتی پس از حذف شیء حفظ می‌کنند. اگر hash() برای نخستین بار تنها پس از حذف شیء فراخوانی شود، این فراخوانی TypeError را پرتاب می‌کند.

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

این یک نوع قابل زیرکلاس‌سازی است، نه یک تابع کارخانه‌ای.

ارجاع‌های ضعیف نسبت به نوع شیء‌ای که به آن ارجاع می‌دهند، عام هستند.

__callback__

این ویژگی فقط‌خواندنی، کال‌بکی را برمی‌گرداند که در حال حاضر به ارجاع ضعیف (weakref) مرتبط است. اگر کال‌بکی وجود نداشته باشد یا مورد ارجاعِ weakref دیگر زنده نباشد، این ویژگی مقدار None خواهد داشت.

تغییر یافته در نسخه‌ی 3.4: ویژگی __callback__ اضافه شد.

weakref.proxy(object[, callback])

یک پراکسی به object برمی‌گرداند که از یک ارجاع ضعیف استفاده می‌کند. این موضوع از استفاده از پراکسی در بیشتر زمینه‌ها پشتیبانی می‌کند، بدون این‌که به ارجاع‌گشایی (dereferencing) صریح که با اشیاء ارجاع ضعیف به کار می‌رود نیاز باشد. نوع شیء برگردانده‌شده، بسته به این‌که object قابل فراخوانی باشد یا نه، ProxyType یا CallableProxyType خواهد بود. اشیاء پراکسی صرف‌نظر از مورد ارجاع، hashable نیستند؛ این امر از تعدادی مشکل مرتبط با ماهیت اساساً تغییرپذیرشان جلوگیری می‌کند و مانع از استفاده از آن‌ها به‌عنوان کلیدهای دیکشنری می‌شود. callback همان پارامتر هم‌نام در تابع ref() است.

دسترسی به یک ویژگی از شیء پراکسی پس از زباله‌روبی شدن مرجع آن، باعث پرتاب ReferenceError می‌شود.

تغییر یافته در نسخه‌ی 3.8: پشتیبانی از عملگرها در اشیای پراکسی گسترش یافت تا شامل عملگرهای ضرب ماتریسی @ و @= شود.

weakref.getweakrefcount(object)

تعداد ارجاع‌های ضعیف و پراکسی‌هایی را که به object ارجاع می‌دهند، برمی‌گرداند.

weakref.getweakrefs(object)

فهرستی از همه‌ی اشیای ارجاع ضعیف و پراکسی که به object ارجاع می‌دهند برمی‌گرداند.

class weakref.WeakKeyDictionary([dict])

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

توجه داشته باشید که هنگامی که کلیدی با مقدار برابر با یک کلید موجود (اما با هویت نابرابر) در دیکشنری درج می‌شود، مقدار جایگزین می‌شود، اما کلید موجود جایگزین نمی‌شود. به همین دلیل، هنگامی که مرجع کلید اصلی حذف شود، آیتم موجود در دیکشنری نیز حذف می‌شود:

>>> class T(str): pass
...
>>> k1, k2 = T(), T()
>>> d = weakref.WeakKeyDictionary()
>>> d[k1] = 1   # d = {k1: 1}
>>> d[k2] = 2   # d = {k1: 2}
>>> del k1      # d = {}

یک راه‌حل موقت این است که کلید را پیش از انتساب مجدد حذف کنید:

>>> class T(str): pass
...
>>> k1, k2 = T(), T()
>>> d = weakref.WeakKeyDictionary()
>>> d[k1] = 1   # d = {k1: 1}
>>> del d[k1]
>>> d[k2] = 2   # d = {k2: 2}
>>> del k1      # d = {k2: 2}

تغییر یافته در نسخه‌ی 3.9: پشتیبانی از عملگرهای | و |=، همان‌طور که در PEP 584 مشخص شده است، افزوده شد.

اشیای WeakKeyDictionary یک متد اضافه دارند که ارجاع‌های داخلی را به‌طور مستقیم آشکار می‌کند. تضمینی نیست که این ارجاع‌ها در زمان استفاده «زنده» باشند، بنابراین نتیجه‌ی فراخوانی ارجاع‌ها باید پیش از استفاده بررسی شود. این می‌تواند برای اجتناب از ایجاد ارجاع‌هایی به کار رود که باعث می‌شود زباله‌روبی کلیدها را بیش از حد نیاز نگه دارد.

WeakKeyDictionary.keyrefs()

یک پیمایش‌پذیر از ارجاع‌های ضعیف به کلیدها برمی‌گرداند.

class weakref.WeakValueDictionary([dict])

کلاس نگاشتی که به مقادیر به‌صورت ضعیف ارجاع می‌دهد. هنگامی که دیگر هیچ ارجاع قوی به مقدار وجود نداشته باشد، ورودی‌های دیکشنری حذف می‌شوند.

تغییر یافته در نسخه‌ی 3.9: پشتیبانی از عملگرهای | و |=، همان‌طور که در PEP 584 مشخص شده است، افزوده شد.

اشیای WeakValueDictionary یک متد اضافی دارند که همان مشکلات متد WeakKeyDictionary.keyrefs() را دارد.

WeakValueDictionary.valuerefs()

یک پیمایش‌پذیر از ارجاع‌های ضعیف به مقادیر برمی‌گرداند.

class weakref.WeakSet([elements])

کلاس مجموعه‌ای که ارجاع‌های ضعیف به عناصر خود را نگه می‌دارد. هرگاه دیگر هیچ ارجاع قوی‌ای به یک عنصر وجود نداشته باشد، آن عنصر حذف می‌شود.

class weakref.WeakMethod(method[, callback])

یک زیرکلاس سفارشی از ref که یک ارجاع ضعیف به یک متد مقید را شبیه‌سازی می‌کند (یعنی متدی که در یک کلاس تعریف شده و روی یک نمونه جست‌وجو می‌شود). از آن‌جا که متد مقید گذرا است، یک ارجاع ضعیف استاندارد نمی‌تواند آن را نگه دارد. WeakMethod کد ویژه‌ای برای بازسازی متد مقید دارد تا پیش از آن‌که شیء یا تابع اصلی از بین برود:

>>> class C:
...     def method(self):
...         print("method called!")
...
>>> c = C()
>>> r = weakref.ref(c.method)
>>> r()
>>> r = weakref.WeakMethod(c.method)
>>> r()
<bound method C.method of <__main__.C object at 0x7fc859830220>>
>>> r()()
method called!
>>> del c
>>> gc.collect()
0
>>> r()
>>>

callback همان پارامتر هم‌نام در تابع ref() است.

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

class weakref.finalize(obj, func, /, *args, **kwargs)

یک شیء نهایی‌سازٔ فراخوانی‌پذیر برمی‌گرداند که هنگام زباله‌روبی obj فراخوانی می‌شود. برخلاف یک ارجاع ضعیف معمولی، نهایی‌ساز همیشه تا زمانی که شیء ارجاع‌شده زباله‌روبی شود باقی می‌ماند و مدیریت چرخه‌ی عمر را بسیار ساده‌تر می‌کند.

یک نهایی‌ساز تا پیش از آنکه فراخوانی شود (چه به‌صراحت و چه در زمان زباله‌روبی)، زنده در نظر گرفته می‌شود و پس از آن مرده است. فراخوانی یک نهایی‌ساز زنده، نتیجه ارزیابی func(*arg, **kwargs) را برمی‌گرداند، در حالی که فراخوانی یک نهایی‌ساز مرده None را برمی‌گرداند.

استثناهایی که توسط کال‌بک‌های نهایی‌ساز در حین زباله‌روبی پرتاب می‌شوند، در خروجی خطای استاندارد نمایش داده خواهند شد، اما نمی‌توانند انتشار یابند. آن‌ها به همان شیوه‌ای مدیریت می‌شوند که استثناهای پرتاب‌شده از متد __del__() یک شیء یا کال‌بک یک ارجاع ضعیف مدیریت می‌شوند.

When the program exits (or more generally, at interpreter shutdown), each remaining live finalizer is called unless its atexit attribute has been set to false. They are called in reverse order of creation.

یک نهایی‌ساز هرگز کال‌بک خود را در بخش پایانی خاموش شدن مفسر فراخوانی نمی‌کند، هنگامی که متغیرهای سراسری ماژول ممکن است با None جایگزین شده باشند.

__call__()

اگر self زنده باشد، آن را به‌عنوان مرده علامت‌گذاری می‌کند و نتیجه فراخوانی func(*args, **kwargs) را برمی‌گرداند. اگر self مرده باشد، None را برمی‌گرداند.

detach()

اگر self زنده باشد، آن را به‌عنوان مرده علامت‌گذاری می‌کند و تاپل (obj, func, args, kwargs) را برمی‌گرداند. اگر self مرده باشد، None را برمی‌گرداند.

peek()

اگر self زنده باشد، تاپل (obj, func, args, kwargs) را برمی‌گرداند. اگر self مرده باشد، None را برمی‌گرداند.

alive

ویژگی‌ای که اگر نهایی‌ساز زنده باشد، true است، در غیر این صورت false است.

atexit

A writable boolean property which by default is true. At interpreter shutdown, all remaining live finalizers for which atexit is true are called in reverse order of creation.

توجه

مهم است اطمینان حاصل کنید که func، args و kwargs مالک هیچ ارجاعی به obj نباشند، چه به‌طور مستقیم و چه به‌طور غیرمستقیم، زیرا در غیر این صورت obj هرگز زباله‌روبی نخواهد شد. به‌ویژه، func نباید یک متد مقید (bound method) به obj باشد.

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

class weakref.ReferenceType

شیء نوع برای اشیاء ارجاع ضعیف.

class weakref.ProxyType

شیء نوع برای پراکسی‌های اشیایی که فراخوانی‌پذیر نیستند.

class weakref.CallableProxyType

شیء نوع برای پراکسی‌های اشیای فراخوانی‌پذیر.

weakref.ProxyTypes

دنباله‌ای شامل تمام اشیای نوع مربوط به پراکسی‌ها. این موضوع می‌تواند بررسی اینکه آیا یک شیء پراکسی است را بدون وابستگی به نام‌بردن از هر دو نوع پراکسی ساده‌تر کند.

همچنین ملاحظه نمائید

PEP 205 - ارجاع‌های ضعیف

پیشنهاد و دلایل این ویژگی، شامل پیوندهایی به پیاده‌سازی‌های پیشین و اطلاعاتی درباره ویژگی‌های مشابه در زبان‌های دیگر.

اشیای ارجاع ضعیف

اشیای ارجاع ضعیف هیچ متد و هیچ ویژگی‌ای به جز ref.__callback__ ندارند. یک شیء ارجاع ضعیف به شما امکان می‌دهد که با فراخوانی این شیء، مورد ارجاع را، اگر هنوز وجود داشته باشد، به دست آورید:

>>> import weakref
>>> class Object:
...     pass
...
>>> o = Object()
>>> r = weakref.ref(o)
>>> o2 = r()
>>> o is o2
True

اگر مورد ارجاع دیگر وجود نداشته باشد، فراخوانی شیء ارجاع None را برمی‌گرداند:

>>> del o, o2
>>> print(r())
None

بررسی زنده بودن یک شیء ارجاع ضعیف (weak reference) باید با استفاده از عبارت ref() is not None انجام شود. معمولاً، کد برنامه‌ای که نیاز به استفاده از یک شیء ارجاع دارد باید از این الگو پیروی کند:

# r is a weak reference object
o = r()
if o is None:
    # referent has been garbage collected
    print("Object has been deallocated; can't frobnicate.")
else:
    print("Object is still live!")
    o.do_something_useful()

استفاده از یک بررسی جداگانه برای «زنده بودن» باعث ایجاد شرایط رقابتی در برنامه‌های چندنخی می‌شود؛ نخ دیگری می‌تواند باعث شود یک ارجاع ضعیف پیش از فراخوانی‌اش نامعتبر شود؛ الگوی نشان‌داده‌شده در بالا هم در برنامه‌های چندنخی و هم در برنامه‌های تک‌نخی ایمن است.

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

این مثال نشان می‌دهد که چگونه می‌توان از یک زیرکلاس از ref برای ذخیره اطلاعات بیشتر درباره یک شیء و تأثیرگذاری بر مقداری که هنگام دسترسی به شیء ارجاع‌شده برگردانده می‌شود، استفاده کرد:

import weakref

class ExtendedRef(weakref.ref):
    def __init__(self, ob, callback=None, /, **annotations):
        super().__init__(ob, callback)
        self.__counter = 0
        for k, v in annotations.items():
            setattr(self, k, v)

    def __call__(self):
        """Return a pair containing the referent and the number of
        times the reference has been called.
        """
        ob = super().__call__()
        if ob is not None:
            self.__counter += 1
            ob = (ob, self.__counter)
        return ob

مثال

این مثال ساده نشان می‌دهد که یک برنامه چگونه می‌تواند از شناسه‌های اشیاء برای بازیابی اشیایی که پیش‌تر دیده است استفاده کند. سپس می‌توان از شناسه‌های اشیاء در سایر ساختارهای داده استفاده کرد، بدون آنکه اشیاء مجبور به زنده ماندن شوند، اما اگر زنده بمانند، همچنان می‌توان آن‌ها را با شناسه بازیابی کرد.

import weakref

_id2obj_dict = weakref.WeakValueDictionary()

def remember(obj):
    oid = id(obj)
    _id2obj_dict[oid] = obj
    return oid

def id2obj(oid):
    return _id2obj_dict[oid]

اشیای نهایی‌ساز (Finalizer Objects)

مزیت اصلی استفاده از finalize این است که ثبت یک کال‌بک را بدون نیاز به نگه‌داری شیء نهایی‌ساز بازگشتی ساده می‌کند. برای مثال

>>> import weakref
>>> class Object:
...     pass
...
>>> kenny = Object()
>>> weakref.finalize(kenny, print, "You killed Kenny!")
<finalize object at ...; for 'Object' at ...>
>>> del kenny
You killed Kenny!

همچنین می‌توان نهایی‌ساز را مستقیماً فراخوانی کرد. با این حال، نهایی‌ساز کال‌بک را حداکثر یک بار فراخوانی می‌کند.

>>> def callback(x, y, z):
...     print("CALLBACK")
...     return x + y + z
...
>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> assert f.alive
>>> assert f() == 6
CALLBACK
>>> assert not f.alive
>>> f()                     # callback not called because finalizer dead
>>> del obj                 # callback not called because finalizer dead

شما می‌توانید با استفاده از متد detach() آن، ثبت یک نهایی‌ساز را لغو کنید. این کار نهایی‌ساز را از بین می‌برد و آرگومان‌هایی را که هنگام ایجاد آن به سازنده داده شده بودند، برمی‌گرداند.

>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> f.detach()
(<...Object object ...>, <function callback ...>, (1, 2), {'z': 3})
>>> newobj, func, args, kwargs = _
>>> assert not f.alive
>>> assert newobj is obj
>>> assert func(*args, **kwargs) == 6
CALLBACK

مگر اینکه ویژگی atexit را روی False تنظیم کنید، نهایی‌ساز در صورتی که هنوز زنده باشد، هنگام خروج برنامه فراخوانی خواهد شد. برای مثال

>>> obj = Object()
>>> weakref.finalize(obj, print, "obj dead or exiting")
<finalize object at ...; for 'Object' at ...>
>>> exit()
obj dead or exiting

مقایسه‌ی نهایی‌سازها با متدهای __del__()

فرض کنید می‌خواهیم کلاسی بسازیم که نمونه‌های آن نشان‌دهنده‌ی پوشه‌های موقت هستند. این پوشه‌ها باید همراه با محتویاتشان حذف شوند، زمانی که نخستین مورد از رویدادهای زیر رخ دهد:

  • شیء زباله‌روبی می‌شود،

  • متد remove() شیء فراخوانی می‌شود، یا

  • برنامه خارج می‌شود.

ممکن است بخواهید کلاس را با استفاده از متد __del__() به‌صورت زیر پیاده‌سازی کنید:

class TempDir:
    def __init__(self):
        self.name = tempfile.mkdtemp()

    def remove(self):
        if self.name is not None:
            shutil.rmtree(self.name)
            self.name = None

    @property
    def removed(self):
        return self.name is None

    def __del__(self):
        self.remove()

از پایتون 3.4 به بعد، متدهای __del__() دیگر مانع از زباله‌روبی شدن چرخه‌های ارجاع نمی‌شوند، و متغیرهای سراسری ماژول دیگر در interpreter shutdown به‌اجبار به None تنظیم نمی‌شوند. بنابراین این کد باید بدون هیچ مشکلی در CPython اجرا شود.

با این حال، مدیریت متدهای __del__() به‌طور معروفی به پیاده‌سازی وابسته است، زیرا به جزئیات داخلی پیاده‌سازی زباله‌روبی مفسر بستگی دارد.

یک جایگزین مطمئن‌تر می‌تواند تعریف یک نهایی‌ساز باشد که فقط به توابع و اشیاء خاصی که به آن‌ها نیاز دارد ارجاع می‌دهد، نه اینکه به وضعیت کامل شیء دسترسی داشته باشد:

class TempDir:
    def __init__(self):
        self.name = tempfile.mkdtemp()
        self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)

    def remove(self):
        self._finalizer()

    @property
    def removed(self):
        return not self._finalizer.alive

با این تعریف، نهایی‌ساز ما فقط ارجاعی به جزئیات مورد نیاز برای پاک‌سازی مناسب پوشه دریافت می‌کند. اگر شیء هرگز زباله‌روبی نشود، نهایی‌ساز همچنان در هنگام خروج فراخوانی خواهد شد.

مزیت دیگر نهایی‌سازهای مبتنی بر weakref این است که می‌توان از آن‌ها برای ثبت نهایی‌سازها برای کلاس‌هایی استفاده کرد که تعریف آن‌ها توسط یک شخص ثالث کنترل می‌شود، مانند اجرای کد هنگامی که یک ماژول از حافظه خارج می‌شود:

import weakref, sys
def unloading_module():
    # implicit reference to the module globals from the function body
weakref.finalize(sys.modules[__name__], unloading_module)

توجه

اگر درست هنگام خروج برنامه، یک شیء نهایی‌ساز را در یک نخ daemon (daemonic thread) ایجاد کنید، این احتمال وجود دارد که finalizer در زمان خروج فراخوانی نشود. با این حال، در یک نخ daemon، atexit.register()، try: ... finally: ... و with: ... نیز تضمین نمی‌کنند که پاک‌سازی انجام شود.