SpecialistOff.NET / Вопросы / Статьи / Фрагменты кода / Резюме / Метки / Помощь / Файлы
НазадМетки: python
Cleanup happens to globals by setting them to None (what about locals?). Does the act of setting them to None cause __del__ to be called, or is __del__ called by Python before a global is set to None?
Consider the following:
class Counter:
    Count = 0   # This represents the count of objects of this class
    def __init__(self, name):
        self.name = name
        print name, 'created'
        Counter.Count += 1
    def __del__(self):
        print self.name, 'deleted'
        Counter.Count -= 1
        if Counter.Count == 0:
            print 'Last Counter object deleted'
        else:
            print Counter.Count, 'Counter objects remaining'
x = Counter("First")
del x
Without the final del, you get an exception. Shouldn’t the normal cleanup process take care of this?
From the Python docs regarding __del__:
Warning: Due to the precarious circumstances under which __del__() methods are invoked, exceptions that occur during their execution are ignored, and a warning is printed to sys.stderr instead. Also, when __del__() is invoked in response to a module being deleted (e.g., when execution of the program is done), other globals referenced by the __del__() method may already have been deleted. For this reason, __del__() methods should do the absolute minimum needed to maintain external invariants.
Without the explicit call to del, __del__ is only called at the end of the program, Counter and/or Count may have already been GC-ed by the time __del__is called (the order in which objects are collected is not deterministic). The exception means that Counter has already been collectd. You can’t do anything particularly fancy with __del__.
There are two possible solutions here.
1. Use an explicit finalizer method, such as close() for file objects.
- Use weak references.
Here’s an example of weak references, using a WeakValueDictionary and the trick of mapping id(self) to self:
from weakref import WeakValueDictionary
class Counter:
    _instances = WeakValueDictionary()
    @property
    def Count(self):
        return len(self._instances)
    def __init__(self, name):
        self.name = name
        self._instances[id(self)] = self
        print name, 'created'
    def __del__(self):
        print self.name, 'deleted'
        if self.Count == 0:
            print 'Last Counter object deleted'
        else:
            print self.Count, 'Counter objects remaining'
x = Counter("First")
Now cleanup happens properly without the need for an explicit call to del.