Почему в этом случае новый класс стиля и класс старого стиля имеют другое поведение?

Я нашел что-то интересное, вот фрагмент кода:

class A(object): def __init__(self): print "A init" def __del__(self): print "A del" class B(object): a = A() 

Если я запустил этот код, я получу:

 A init 

Но если я изменю class B(object) на class B() , я получу:

 A init A del 

Я нашел примечание в документе __del__ :

Не гарантируется, что методы del () вызываются для объектов, которые все еще существуют, когда переводчик завершает работу.

Тогда, я думаю, это потому, что Ba все еще ссылается (ссылка на класс B ), когда существует интерпретатор.

Итак, я добавил del B до того, как интерпретатор существует вручную, и затем я обнаружил, a.__del__() был вызван a.__del__() .

Теперь я немного смущен этим. Почему при использовании старого класса стиля вызывается a.__del__() ? Почему у классов нового и старого стиля другое поведение?

Я нашел здесь аналогичный вопрос, но я думаю, что ответы недостаточно ясны.

One Solution collect form web for “Почему в этом случае новый класс стиля и класс старого стиля имеют другое поведение?”

TL; DR: это старая проблема в CPython, которая была наконец исправлена ​​в CPython 3.4 . Объекты, хранящиеся в режиме реального времени по ссылочным циклам, на которые ссылаются глобальные модули модуля, не были должным образом завершены на выходе интерпретатора в версиях CPython до 3.4. Классы нового стиля имеют неявные циклы в своих экземплярах type ; классы старого стиля (типа classobj ) не имеют неявных ссылочных циклов.

Несмотря на то, что в этом случае исправлено, документация CPython 3.4 по-прежнему рекомендует не зависеть от __del__ что __del__ на выходе интерпретатора, – подумайте о себе.


В новых классах классов есть ссылочные циклы сами по себе: в первую очередь

 >>> class A(object): ... pass >>> A.__mro__[0] is A True 

Это означает, что они не могут быть немедленно удалены *, но только при запуске сборщика мусора. Поскольку ссылка на них удерживается главным модулем, они остаются в памяти до выключения интерпретатора. В конце, во время очистки модуля, все глобальные имена модуля в основном настроены так, чтобы они указывали на « None , и в зависимости от того, какие объекты имели счетчики ссылок, уменьшились до нуля (например, старый стиль), также были удалены. Тем не менее, классы нового стиля, имеющие ссылочные циклы, не будут выпущены / завершены этим.

Циклический сборщик мусора не будет запускаться на выходе интерпретатора (что разрешено документацией CPython :

Не гарантируется, что __del__() вызываются для объектов, которые все еще существуют, когда переводчик завершает работу.


Теперь классы старого стиля в Python 2 не имеют неявных циклов. Когда код очистки / завершения модуля CPython устанавливает глобальные переменные в None , единственная оставшаяся ссылка на класс B отбрасывается; то B удаляется, и последняя ссылка на a отбрасывается, а также завершается.


Чтобы продемонстрировать тот факт, что классы нового стиля имеют циклы и требуют GC-развертки, тогда как в классах старого стиля нет, вы можете попробовать следующую программу в CPython 2 (у CPython 3 уже нет классов старого стиля):

 import gc class A(object): def __init__(self): print("A init") def __del__(self): print("A del") class B(object): a = A() del B print("About to execute gc.collect()") gc.collect() 

С B как класс нового стиля, как указано выше, вывод

 A init About to execute gc.collect() A del 

С B как класс старого стиля ( class B: 🙂 выход

 A init A del About to execute gc.collect() 

То есть класс нового стиля был удален только после gc.collect() хотя последняя внешняя ссылка на него уже была отброшена; но класс старого стиля был удален мгновенно.


Большая часть этого уже исправлена в Python 3.4 : благодаря PEP 442 , который включал процедуру выключения модуля на основе кода GC . Теперь даже при выходе интерпретатора глобалы модуля завершаются с помощью обычной сборки мусора. Если вы запустите свою программу под Python 3.4, программа распечатает

 A init A del 

В то время как с Python <= 3.3 он будет печатать

 A init 

( __del__ в виду, что другие реализации все еще могут или не могут выполнять __del__ в данный момент, независимо от того, __del__ версия их выше, на или ниже, 3.4)

  • Python's Passing by References
  • Каков приоритет импорта имени, подмодуля или подпакета из пакета в python 2.7?
  • Почему hardcoding этот список медленнее, чем его вычисление?
  • Почему проверка isinstance (что-то, Mapping) настолько медленная?
  • Почему «1000000000000000 в диапазоне (1000000000000001)» так быстро в Python 3?
  • Как работает str (список)?
  • Идентификация нормализации: почему микро-знак преобразован в греческую букву mu?
  • Почему для _ в диапазоне (n) медленнее, чем для _ в * n?
  • Python - лучший язык программирования в мире.