Що таке метод __del__, як його називати?


108

Я читаю код. Існує клас, у якому __del__визначено метод. Я зрозумів, що цей метод використовується для знищення екземпляра класу. Однак я не можу знайти місце, де використовується цей метод. Основною причиною для цього є те , що я не знаю , як використовується цей метод, ймовірно , не так: obj1.del(). Отже, мої запитання - як викликати __del__метод?

Відповіді:


168

__del__є фіналізатором . Він називається, коли об'єкт збирається сміттям, що відбувається в якийсь момент після видалення всіх посилань на об'єкт.

У простому випадку це може бути відразу після того, як ви скажете, del xабо, якщо xце локальна змінна, після закінчення функції. Зокрема, якщо немає кругових посилань, CPython (стандартна реалізація Python) негайно збиратиме сміття.

Однак, це деталізація реалізації CPython. Єдиною необхідною властивістю збору сміття Python є те, що це відбувається після видалення всіх посилань, тому це може не бути необхідним відразу після цього, а може і не статися взагалі. .

Навіть більше, змінні можуть тривалий час жити з багатьох причин , наприклад, розповсюджувальний виняток або інтроспекція модуля можуть підтримувати кількість змінних посилань більше 0. Також змінна може бути частиною циклу посилань - CPython із включеним збиранням сміття на перервах більшість , але не всі, такі цикли, та й то лише періодично.

Оскільки у вас немає гарантії, що вона виконується, ніколи не слід ставити код, який потрібно запустити __del__()- натомість цей код належить до finallyпункту tryблоку або до менеджера контексту у withвиписці. Однак є дійсні випадки використання для __del__: наприклад, якщо на Xпосилання на об'єкт посилається Yі зберігається копія Yпосилання в глобальному cache( cache['X -> Y'] = Y), то було б ввічливо X.__del__також видалити запис кешу.

Якщо ви знаєте , що деструкція забезпечує (в порушення зазначеного вище орієнтира) необхідної очищення, ви можете назвати це безпосередньо , так як немає нічого особливого, як метод: x.__del__(). Очевидно, ви повинні робити це лише в тому випадку, якщо знаєте, що не проти подзвонити двічі. Або, в крайньому випадку, ви можете переглянути цей метод за допомогою

type(x).__del__ = my_safe_cleanup_method  

5
Ви говорите, що особливість CPython видалення об'єкта одразу після того, як його посилання зменшиться до нуля, є "деталлю реалізації". Я не переконаний. Чи можете ви надати посилання для резервного копіювання цієї претензії? (Я маю на увазі, жирний шрифт сам по собі є досить переконливим, але посилання - це найближча секунда ... :-)
Стюарт Берг

14
Деталі реалізації CPython: CPython в даний час використовує схему підрахунку посилань з (необов'язково) затримкою виявлення циклічно пов'язаного сміття, ... Інші реалізації діють по-різному, і CPython може змінюватися. ( docs.python.org/2/reference/datamodel.html )
ilya n.

Що з __exit__цим у цьому контексті? Запускається після або перед __del__чи разом?
самотній

1
Чи включається "може не статися взагалі", коли програма припиняється?
Енді Хайден

1
@AndyHayden: __del__методи можуть не працювати навіть при завершенні програми, і навіть коли вони запускаються при завершенні, написання __del__методу, який працює належним чином навіть тоді, коли інтерпретатор зайнятий саморуйнуванням навколо вас, вимагає більш ретельного кодування, ніж застосовують багато програмістів. (Засіб очищення CPython зазвичай отримує __del__методи для запуску при відключенні інтерпретатора, але все ж є випадки, коли цього недостатньо. Нитки Демона, глобальні глобальні рівні та об'єкти, __del__створені в іншому, __del__можуть призвести до __del__запуску методів.)
користувач2357112 підтримує Моніка

80

Я написав відповідь на інше питання, хоча це більш точне питання до нього.

Як працюють конструктори та деструктори?

Ось злегка сумнівна відповідь.

Не використовуйте __del__. Це не C ++ або мова, створена для деструкторів. __del__Метод дійсно повинен піти в Python 3.x, хоча я впевнений , що хто - то знайде випадок використання , який має сенс. Якщо вам потрібно скористатися __del__, пам’ятайте про основні обмеження на http://docs.python.org/reference/datamodel.html :

  • __del__називається, коли збирач сміття збирає предмети, а не тоді, коли ви втрачаєте останню посилання на об'єкт, а не під час виконання del object.
  • __del__ відповідає за дзвінок будь-якого __del__ в надкласовому класі, хоча незрозуміло, чи це в порядку розв'язання методу (MRO) або просто виклик кожного суперкласу.
  • Маючи на __del__увазі, що збирач сміття відмовляється від виявлення та очищення будь-яких циклічних посилань, таких як втрата останньої посилання на пов'язаний список. Ви можете отримати список об'єктів, проігнорованих із gc.garbage. Ви можете іноді використовувати слабкі посилання, щоб взагалі уникнути циклу. Про це обговорюється час від часу: див. Http://mail.python.org/pipermail/python-ideas/2009-O жовтня/006194.html .
  • The __del__Функція може обдурити, зберігаючи посилання на об'єкт, і зупинити вивіз сміття.
  • Винятки, явно __del__вказані в , ігноруються.
  • __del__доповнює __new__набагато більше, ніж __init__. Це стає заплутаним. Див. Http://www.algorithm.co.il/blogs/programming/python-gotchas-1- del -is-not-the- suprot-of - init / для отримання пояснень та отриманих даних.
  • __del__не є "добре коханою" дитиною в Python. Ви помітите, що документація на sys.exit () не вказує, чи збирається сміття перед виходом, і існує чимало незвичайних проблем. Виклик у __del__глобальних мережах викликає незвичайні проблеми впорядкування, наприклад, http://bugs.python.org/issue5099 . Слід __del__зателефонувати навіть у випадку __init__невдачі? Дивіться http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423 довгу нитку.

Але, з іншого боку:

І моя песональна причина не подобається __del__функції.

  • Кожен раз, коли хтось виховує __del__це, перетворюється на тридцять повідомлень про непорозуміння.
  • Він розбиває ці предмети в дзен Python:
    • Просте - краще, ніж складне.
    • Особливі випадки недостатньо спеціальні для порушення правил.
    • Помилки ніколи не повинні проходити мовчки.
    • В умовах неоднозначності відмовтеся від спокуси здогадатися.
    • Має бути один - і бажано лише один - очевидний спосіб зробити це.
    • Якщо реалізацію важко пояснити, це погана ідея.

Отже, знайдіть причину не використовувати __del__.


6
Навіть якщо питання не точно: чому нам не користуватися __del__, а як телефонувати __del__, ваша відповідь цікава.
nbro

Дякую. Іноді найкраща ідея - ухилитися від жахливих ідей.
Чарльз Мерріам

В інших новинах я забув згадати, що PyPy (більш швидкий інтерпретатор для довших запущених додатків) зламається на del .
Чарльз Мерріам

Дякую @Gloin за оновлення пошкодженого посилання!
Чарльз Мерріам

@CharlesMerriam Дякуємо Вас за відповідь!
Том Берроуз

13

__del__Метод, він буде викликатися , коли об'єкт сміття. Зауважте, що не обов'язково гарантовано називатися. Наступний код сам по собі не обов'язково робити це:

del obj

Причина в тому, що delпросто зменшується кількість посилань на одиницю. Якщо щось інше має посилання на об'єкт,__del__ не дзвонить.

Однак є кілька застережень щодо використання __del__. Як правило, вони просто не дуже корисні. Мені це здається більше схожим на те, що ти хочеш скористатись методом закриття або, можливо, із заявою .

Дивіться документацію python щодо __del__методів .

Ще одне, що слід зазначити: __del__методи можуть перешкоджати збору сміття при надмірному використанні. Зокрема, циркулярна посилання, яка має більше одного об'єкта __del__методом, не збирає сміття. Це тому, що сміттєзбірник не знає, кого першим зателефонувати. Додаткову інформацію див. У документації на модуль gc .


8

The __del__Метод (примітка правопис!) Викликається , коли ваш об'єкт остаточно зруйнований. Технічно кажучи (в cPython), коли немає більше посилань на ваш об'єкт, тобто коли він виходить за межі області.

Якщо ви хочете видалити свій об'єкт і тим самим викликати __del__метод використання

del obj1

який видалить об'єкт (за умови, що інших посилань на нього не було).

Я пропоную вам написати такий маленький клас

class T:
    def __del__(self):
        print "deleted"

І досліджуйте в інтерпретаторі пітона, напр

>>> a = T()
>>> del a
deleted
>>> a = T()
>>> b = a
>>> del b
>>> del a
deleted
>>> def fn():
...     a = T()
...     print "exiting fn"
...
>>> fn()
exiting fn
deleted
>>>   

Зауважте, що jython та ironpython мають різні правила щодо того, коли саме об’єкт видаляється та __del__викликається. Не вважається хорошою практикою використання, __del__хоча через це і той факт, що об'єкт та його оточення можуть опинитися в невідомому стані при його виклику. Це не абсолютно гарантовано __del__буде називатися - інтерпретатор може вийти різними способами, не видаляючи всіх об'єктів.


1
порівняно зі stackoverflow.com/a/2452895/611007 та stackoverflow.com/a/1481512/611007 , use del obj1здається, погана ідея, на яку можна покластися.
n611x007

0

Як згадувалося раніше, __del__функціонал дещо ненадійний. У випадках, коли це може здатися корисним, замість цього скористайтеся методами __enter__та __exit__методами. Це спричинить поведінку, подібну до with open() as f: passсинтаксису, який використовується для доступу до файлів. __enter__автоматично викликається при введенні в область дії with, а __exit__автоматично викликається при виході з нього. Дивіться це питання для більш детальної інформації.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.