Когда классы Python и атрибуты класса собираются мусором?
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
производит следующий вывод:
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 и атрибуты класса собираются мусором?
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
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/
Надеюсь, поможет.:)