Jaka jest metoda __del__, jak ją nazwać?

109

Czytam kod. Istnieje klasa, w której __del__zdefiniowano metodę. Dowiedziałem się, że ta metoda służy do niszczenia instancji klasy. Nie mogę jednak znaleźć miejsca, w którym ta metoda jest stosowana. Głównym tego powodem jest to, że nie wiem, jak ta metoda jest stosowana, chyba nie tak: obj1.del(). Więc moje pytania to jak wywołać tę __del__metodę?

Verrtex
źródło

Odpowiedzi:

168

__del__jest finalizatorem . Jest wywoływana, gdy obiekt jest odśmiecany, co ma miejsce w pewnym momencie po usunięciu wszystkich odwołań do obiektu.

W prostym przypadku może to nastąpić zaraz po tym, jak powiesz del xlub, jeśli xjest to zmienna lokalna, po zakończeniu funkcji. W szczególności, jeśli nie ma odwołań cyklicznych, CPython (standardowa implementacja Pythona) natychmiast usunie elementy bezużyteczne.

Jest to jednak szczegół implementacji języka CPython. Jedyną wymaganą właściwością wyrzucania elementów bezużytecznych w Pythonie jest to, że dzieje się to po usunięciu wszystkich odniesień, więc może to nie być konieczne zaraz potem i może się nie zdarzyć w ogóle .

Co więcej, zmienne mogą istnieć przez długi czas z wielu powodów , np. Propagujący wyjątek lub introspekcja modułu może utrzymywać liczbę odwołań do zmiennej większą niż 0. Ponadto zmienna może być częścią cyklu odwołań - CPython z włączonym czyszczeniem pamięci powoduje przerwanie większości ale nie wszystkie takie cykle i nawet wtedy tylko okresowo.

Ponieważ nie masz gwarancji, że zostanie wykonany, nigdy nie należy umieszczać kodu, na który chcesz się natknąć __del__()- zamiast tego kod ten należy do finallyklauzuli trybloku lub do menedżera kontekstu w withinstrukcji. Jednak istnieją ważne przypadki użycia dla __del__: np jeśli obiekt Xodniesienia Y, a także przechowuje kopię Yodniesienia w globalnej cache( cache['X -> Y'] = Y) to byłoby uprzejmy dla X.__del__również usunąć wpis pamięci podręcznej.

Jeśli wiesz , że zapewnia destructor (naruszenie powyższej wytycznej) wymaganego czyszczenia, możesz nazwać to bezpośrednio , ponieważ nie ma nic szczególnego o nim jako o metodzie: x.__del__(). Oczywiście powinieneś to zrobić tylko wtedy, gdy wiesz, że nie przeszkadza Ci dwukrotne wezwanie. Lub w ostateczności możesz przedefiniować tę metodę za pomocą

type(x).__del__ = my_safe_cleanup_method  
ilya n.
źródło
5
Mówisz, że funkcja CPythona polegająca na usuwaniu obiektu natychmiast po zredukowaniu liczby odwołań do zera jest „szczegółem implementacji”. Nie jestem przekonana. Czy możesz podać link do kopii zapasowej tego roszczenia? (Mam na myśli, że pogrubiona czcionka sama w sobie jest całkiem przekonująca, ale linki są na drugim miejscu ... :-)
Stuart Berg
14
Szczegóły implementacji CPythona: CPython obecnie używa schematu zliczania odwołań z (opcjonalnym) opóźnionym wykrywaniem cyklicznie połączonych śmieci, ... Inne implementacje działają inaczej i CPython może się zmienić. ( docs.python.org/2/reference/datamodel.html )
ilya n.
Co __exit__w tym kontekście? Czy działa po, przed __del__czy razem?
lony
1
Czy „może w ogóle się nie wydarzyć” obejmuje zakończenie działania programu?
Andy Hayden
1
@AndyHayden: __del__metody mogą nie działać nawet po zakończeniu programu, a nawet jeśli działają po zakończeniu, napisanie __del__metody, która działa poprawnie, nawet gdy interpreter jest zajęty autodestrukcją wokół ciebie, wymaga staranniejszego kodowania niż wielu programistów. (Czyszczenie CPythona zwykle powoduje, że __del__metody działają po wyłączeniu interpretera, ale wciąż są przypadki, w których to nie wystarcza. Wątki demona, wartości globalne na poziomie C i obiekty __del__utworzone w innym __del__mogą prowadzić do __del__
niedziałania
80

Napisałem odpowiedź na inne pytanie, chociaż jest to bardziej trafne pytanie.

Jak działają konstruktory i destruktory?

Oto nieco uparta odpowiedź.

Nie używaj __del__. To nie jest C ++ ani język stworzony dla destruktorów. Ta __del__metoda naprawdę powinna zniknąć w Pythonie 3.x, chociaż jestem pewien, że ktoś znajdzie przypadek użycia, który ma sens. Jeśli musisz użyć __del__, pamiętaj o podstawowych ograniczeniach na http://docs.python.org/reference/datamodel.html :

  • __del__jest wywoływana, gdy moduł odśmiecania pamięci zbiera obiekty, a nie wtedy, gdy tracisz ostatnie odwołanie do obiektu, a nie podczas wykonywania del object.
  • __del__jest odpowiedzialny za wywołanie dowolnej __del__z nadklasy, chociaż nie jest jasne, czy jest to w kolejności rozwiązywania metod (MRO), czy po prostu wywołuje każdą z nadklasy.
  • Posiadanie __del__oznacza, że ​​moduł odśmiecania pamięci rezygnuje z wykrywania i czyszczenia wszelkich cyklicznych łączy, takich jak utrata ostatniego odniesienia do połączonej listy. Możesz uzyskać listę ignorowanych obiektów z gc.garbage. Czasami możesz użyć słabych odniesień, aby całkowicie uniknąć cyklu. To jest od czasu do czasu dyskutowane: patrz http://mail.python.org/pipermail/python-ideas/2009-October/006194.html .
  • __del__Funkcja może oszukiwać, oszczędzając odniesienie do obiektu, a zatrzymanie zbierania śmieci.
  • Wyjątki jawnie zgłoszone w programie __del__są ignorowane.
  • __del__uzupełnia __new__znacznie więcej niż __init__. To staje się mylące. Zobacz http://www.algorithm.co.il/blogs/programming/python-gotchas-1- del -is-not-the-versus-of- init /, aby uzyskać wyjaśnienie i problemy.
  • __del__nie jest „ukochanym” dzieckiem w Pythonie. Zauważysz, że dokumentacja sys.exit () nie określa, czy śmieci są zbierane przed wyjściem, i jest wiele dziwnych problemów. Wywołanie strony __del__globals powoduje dziwne problemy z kolejnością, np . Http://bugs.python.org/issue5099 . Powinien __del__zadzwonić, nawet jeśli __init__zawiedzie? Długi wątek można znaleźć pod adresem http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423 .

Ale z drugiej strony:

I mój osobisty powód, dla którego nie lubię tej __del__funkcji.

  • Za każdym razem, gdy ktoś __del__ją porusza, zamienia się w trzydzieści wiadomości zawierających zamieszanie.
  • Łamie te elementy w Zen of Python:
    • Proste jest lepsze niż skomplikowane.
    • Specjalne przypadki nie są na tyle wyjątkowe, aby łamać zasady.
    • Błędy nigdy nie powinny przejść bezgłośnie.
    • W obliczu niejasności odrzuć pokusę zgadywania.
    • Powinien być jeden - a najlepiej tylko jeden - oczywisty sposób na zrobienie tego.
    • Jeśli implementacja jest trudna do wyjaśnienia, to zły pomysł.

Więc znajdź powód, żeby go nie używać __del__.

Charles Merriam
źródło
6
Nawet jeśli pytanie nie brzmi dokładnie: dlaczego nie używać __del__, ale jak zadzwonić __del__, Twoja odpowiedź jest interesująca.
nbro
Dzięki. Czasami najlepszym pomysłem jest odejście od okropnych pomysłów.
Charles Merriam
W innych wiadomościach zapomniałem wspomnieć, że PyPy (szybszy interpreter dla dłużej działających aplikacji) zepsuje się na del .
Charles Merriam
Dziękuję @Gloin za aktualizację zepsutego linku!
Charles Merriam
@CharlesMerriam Dziękuję Ci za odpowiedź!
Tom Burrows
13

__del__Metoda będzie wywoływana, gdy obiekt jest garbage zebrane. Pamiętaj jednak, że niekoniecznie jest to gwarantowane. Poniższy kod sam w sobie niekoniecznie to zrobi:

del obj

Powodem jest to, że delpo prostu zmniejsza liczbę referencji o jeden. Jeśli coś innego ma odniesienie do obiektu, __del__nie zostanie wywołane.

Jest jednak kilka zastrzeżeń do używania __del__. Zwykle po prostu nie są zbyt przydatne. Wydaje mi się, że bardziej chcesz użyć metody close, a może instrukcji with .

Zobacz dokumentację Pythona dotyczącą __del__metod .

Jeszcze jedna uwaga: __del__metody mogą uniemożliwić czyszczenie pamięci, jeśli są nadużywane. W szczególności odwołanie cykliczne, które ma więcej niż jeden obiekt z __del__metodą, nie zostanie usunięte. Dzieje się tak, ponieważ odśmiecacz nie wie, do którego z nich zadzwonić jako pierwszy. Więcej informacji znajdziesz w dokumentacji modułu gc .

Jason Baker
źródło
8

__del__Sposób (uwaga pisownia!) Jest wywoływana, gdy obiekt zostanie ostatecznie zniszczony. Z technicznego punktu widzenia (w cPythonie) to znaczy, gdy nie ma już odniesień do twojego obiektu, czyli gdy wychodzi on poza zakres.

Jeśli chcesz usunąć swój obiekt, a tym samym wywołać __del__metodę use

del obj1

co spowoduje usunięcie obiektu (pod warunkiem, że nie było do niego żadnych innych odniesień).

Proponuję napisać taką małą klasę

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

I zbadaj w interprecie Pythona, np

>>> 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
>>>   

Zauważ, że jython i ironpython mają różne reguły co do dokładnego momentu usunięcia obiektu i __del__wywołania. Nie jest to jednak uważane za dobrą praktykę z __del__tego powodu oraz faktu, że obiekt i jego otoczenie mogą znajdować się w nieznanym stanie, gdy jest wywoływany. Nie jest też absolutnie __del__pewne, że zostanie wywołany - interpreter może wyjść na różne sposoby bez usuwania wszystkich obiektów.

Nick Craig-Wood
źródło
1
w porównaniu z stackoverflow.com/a/2452895/611007 i stackoverflow.com/a/1481512/611007 , use del obj1wydaje się, że to zły pomysł, aby polegać.
n611x007,
0

Jak wspomniano wcześniej, __del__funkcjonalność jest nieco zawodna. W przypadkach, w których może się to wydawać przydatne, zamiast tego rozważ użycie metod __enter__i __exit__. Zapewni to zachowanie podobne do with open() as f: passskładni używanej do uzyskiwania dostępu do plików. __enter__jest wywoływana automatycznie po wejściu w zakres with, a __exit__podczas wychodzenia z niej. Zobacz to pytanie, aby uzyskać więcej informacji.

somoria
źródło