4
class Member(object): 
    def __init__(self, identifier): 
     self.identifier = identifier 
     print "Member __init__", self.identifier 

    def __del__(self): 
     print "Member __del__", self.identifier 
     with open("/home/might/" + self.identifier, "w") as outF: 
      outF.write(self.identifier) 

class WithMembers(object): 
    def __init__(self): 
     print "WithMembers __init__" 
     print WithMembers.classMem 
     self.instanceMem = Member("instance mem") 

    def __del__(self): 
     print "WithMembers __del__" 

    classMem = Member("class mem") 

if __name__ == "__main__": 
    print "main" 
    WithMembers() 
    #del WithMembers.classMem  # "Member __del__ class mem" before "end" 
    #del WithMembers    # "Member __del__ class mem" after "end" 
    print "end" 

Приведенный выше код в Hidden.py и работает python Hidden.py производит следующий вывод:Когда классы python и атрибуты класса собирают мусор?

Member __init__ class mem 
main 
WithMembers __init__ 
<__main__.Member object at 0x935aeec> 
Member __init__ instance mem 
WithMembers __del__ 
Member __del__ instance mem 
end 

Я не вижу Member __del__ class mem на выходе или class mem файл, если я не-комментария один из del заявлений , Почему это? Когда собираются классы python и атрибуты класса?

+1

Ваш код не имеет ничего общего со сбором мусора. 'del ' разделяет этот идентификатор, чтобы он больше не указывал на существующий объект. Когда это происходит, вызывается 'your_object .__ del__'. Некоторое время спустя, после того, как на этот объект не осталось НИКАКИХ ССЫЛКИ, это сбор мусора. –

+0

Возможно, никогда, и это хорошо документировано. Подумайте об использовании диспетчера контекстов. – cdarke

+0

@cdarke: действительно хорошо документирован и, возможно, никогда не зависит от версии python. Я провел некоторое исследование и разместил http://stackoverflow.com/a/28552889/4455221 – Might

ответ

2

Об этом сообщается как об ошибке в http://bugs.python.org/issue1545463, зафиксированном в 3,4, но не в обратном (я работал 2,7). Это было также объяснено в http://code.activestate.com/lists/python-list/504216/. См. Ниже для вывода в python 3.5.

Основываясь на выше, я понимаю, что в 2.7 новый класс стиля WithMembers по-прежнему находится вокруг (не очищается GC), когда переводчик выходит. В результате classMem не собирает мусор, потому что WithMembers все еще ссылается на него.

Обратите внимание, что новые классы стиля имеют циклические ссылки на себя от __mro__ и некоторые встроенные дескрипторы (http://bugs.python.org/issue17950). Несмотря на то, что классы нового уровня на модульном уровне считаются мертвыми GC после очистки модуля, вызов GC для их очистки после очистки модуля отключается, поскольку это вызвало слишком много других проблем.

Это не вызывает утечки памяти, поскольку ОС очищает ресурсы после выхода интерпретатора.

class Member(object): 
    def __init__(self, identifier): 
     self.identifier = identifier 
     print("Member __init__ " + self.identifier) 

    def __del__(self): 
     print("Member __del__ " + self.identifier) 
     with open("/home/might/" + self.identifier, "w") as outF: 
      outF.write(self.identifier) 

class WithMembers(object): 
    def __init__(self): 
     print("WithMembers __init__") 
     print(WithMembers.classMem) 
     self.instanceMem = Member("instance mem") 

    def __del__(self): 
     print("WithMembers __del__") 

    classMem = Member("class mem") 

if __name__ == "__main__": 
    print("main") 
    WithMembers() 
    print("end") 

выводит следующее при запуске с python3 Hidden.py:

Member __init__ class mem 
main 
WithMembers __init__ 
<__main__.Member object at 0xb6fc8e2c> 
Member __init__ instance mem 
WithMembers __del__ 
Member __del__ instance mem 
end 
Member __del__ class mem 
Exception ignored in: <bound method Member.__del__ of <__main__.Member object at 0xb6fc8e2c>> 
Traceback (most recent call last): 
    File "class_member_gc.py", line 8, in __del__ 
NameError: name 'open' is not defined 
0

classMem является переменной класса для класса WithMembers, что означает, что он будет использоваться всеми экземплярами этого класса. Это глобальная вещь в Python. Вот почему __del__ класса Участник не получил вызов при выходе из программы.

Возникает вопрос: почему Python не просто устанавливает все количество ссылок на 0 при выходе из программы, чтобы все функции __del__ могли быть вызваны?

Как и C++, который гарантирует, что вызываются деструкторы глобальных переменных. Единственный способ в Python гарантировать это, чтобы обойти все модули и удалить все их переменные. Но это означает, что метод __del__ не может доверять тому, что любые глобальные переменные, которые он может использовать, все еще существуют, поскольку нет способа узнать, в каком порядке должны быть удалены переменные.

Существует два способа принудительного уничтожения classMem.

Первый: del WithMembers.classMem. Это уменьшит количество ссылок на 0 и __del__ будет автоматически вызываться.

Другое делает класс WithMembers старомодным классом (не наследуется от object). Как это:

... 

class WithMembers: 
    def __init__(self): 
     print "WithMembers __init__" 
     print WithMembers.classMem 
     self.instanceMem = Member("instance mem") 
...   

выход будет:

Member __init__ class mem 
main 
WithMembers __init__ 
<__main__.Member object at 0x00000000026C5278> 
Member __init__ instance mem 
WithMembers __del__ 
Member __del__ instance mem 
end 
Member __del__ class mem 

Вот очень полезная ссылка, чтобы помочь вам понять этот ответ лучше. http://www.electricmonk.nl/log/2008/07/07/python-destructor-and-garbage-collection-notes/

Надеюсь, это поможет. :)

+0

Спасибо за указание на классы старого стиля, которые привели меня к ссылкам, приведенным в http://stackoverflow.com/a/28552889/4455221. Я не думаю, что полностью согласен со всеми в статье, на которую вы ссылались, особенно с частью «Выход из программы»: http://legacy.python.org/search/hypermail/python-1993/0110.html - это то, что GvR задумал перед тем, __del__' существовал, не рассуждая о текущей реализации. – Might