class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
__del__(self)
powyżej kończy się niepowodzeniem z wyjątkiem wyjątku AttributeError. Rozumiem, że Python nie gwarantuje istnienia „zmiennych globalnych” (danych członka w tym kontekście?), Gdy __del__()
zostanie wywołany. Jeśli tak jest i to jest powód wyjątku, w jaki sposób mogę upewnić się, że obiekt zniszczy się prawidłowo?
python
destructor
wilhelmtell
źródło
źródło
__del__
nie powinien być stosowany jako odpowiednik__init__
. (Tj. Nie jest to „destruktor” w tym sensie, że__init__
jest konstruktorem.Odpowiedzi:
Polecam użycie
with
instrukcji Pythona do zarządzania zasobami, które wymagają oczyszczenia. Problem z używaniem wyraźnegoclose()
stwierdzenia polega na tym, że musisz się martwić, że ludzie w ogóle zapomną go nazwać lub zapomną umieścić go wfinally
bloku, aby zapobiec wyciekom zasobów w przypadku wystąpienia wyjątku.Aby użyć
with
instrukcji, utwórz klasę przy użyciu następujących metod:W powyższym przykładzie użyłbyś
Następnie, gdy ktoś chciałby skorzystać z twojej klasy, zrobiłby to:
Zmienna package_obj będzie instancją typu Package (jest to wartość zwracana przez
__enter__
metodę). Jego__exit__
metoda zostanie automatycznie wywołana, niezależnie od tego, czy wystąpi wyjątek.Możesz nawet pójść o krok dalej. W powyższym przykładzie ktoś nadal może utworzyć instancję pakietu przy użyciu swojego konstruktora bez użycia
with
klauzuli. Nie chcesz, żeby tak się stało. Możesz to naprawić, tworząc klasę PackageResource, która definiuje metody__enter__
i__exit__
. Następnie klasa Package zostanie zdefiniowana ściśle wewnątrz__enter__
metody i zwrócona. W ten sposób osoba dzwoniąca nigdy nie może utworzyć instancji klasy Package bez użyciawith
instrukcji:Użyłbyś tego w następujący sposób:
źródło
with Resource(param1, param2) as r: # ...
Standardowym sposobem jest użycie
atexit.register
:Należy jednak pamiętać, że spowoduje to zachowanie wszystkich utworzonych instancji do
Package
momentu zakończenia działania Pythona.Demo przy użyciu powyższego kodu zapisanego jako package.py :
źródło
with
? Czy wyraźnie dzwonił__enter__
?) Minusem jest oczywiście to, że jeśli potrzebujesz wyczyszczenia przed pythonem wychodzi, to nie zadziała. W moim przypadku nie obchodzi mnie, czy dzieje się tak, gdy obiekt wykracza poza zasięg, czy też nie, dopóki Python nie wyjdzie. :)atexit.register(self.__exit__)
?__exit__
i użyć menedżera kontekstu? Ponadto__exit__
pobiera dodatkowe argumenty (tj.__exit__(self, type, value, traceback)
), Więc musisz się za nie zgodzić. Tak czy inaczej, wygląda na to, że powinieneś zadać osobne pytanie na SO, ponieważ twoja skrzynka wygląda na nietypową?Jako dodatek do odpowiedzi Clinta możesz uprościć,
PackageResource
używająccontextlib.contextmanager
:Alternatywnie, choć prawdopodobnie nie jako Python, możesz zastąpić
Package.__new__
:i po prostu użyj
with Package(...) as package
.Aby wszystko było krótsze, nazwij swoją funkcję czyszczenia
close
i użyjcontextlib.closing
, w którym to przypadku możesz albo użyć niezmodyfikowanejPackage
klasy,with contextlib.closing(Package(...))
albo zastąpić ją__new__
prostsząI ten konstruktor jest dziedziczony, więc możesz po prostu odziedziczyć, np
źródło
Package.__new__()
metody. A może możemy. Prawdopodobnie moglibyśmy zdefiniować dekorator klasy lub metaklasę generującą dla nas tę płytę grzewczą. Jedzenie dla myśli Pythona.Package
również powinna to zrobić (chociaż jeszcze tego nie testowałem), więc nie powinna być wymagana żadna metaklasa. Chociaż w przeszłości znalazłem kilka ciekawych sposobów używania metaklas ...Package
(lub lepiej klasy o nazwieClosing
) jako swojego rodzica klasy zamiastobject
. Ale nie pytaj mnie, jak wiele dziedziczenia łączy się z tym ...Nie sądzę, że możliwe jest na przykład usunięcie członków przed
__del__
wywołaniem. Domyślam się, że przyczyną konkretnego błędu AttributeError jest gdzie indziej (być może błędnie usuwasz plik self.file w innym miejscu).Jednak, jak zauważyli inni, należy unikać używania
__del__
. Głównym tego powodem jest to, że instancje z__del__
nie będą zbierane śmieci (zostaną one uwolnione dopiero, gdy ich licznik osiągnie 0). Dlatego jeśli instancje są zaangażowane w odwołania cykliczne, będą one istnieć w pamięci tak długo, jak aplikacja będzie działać. (Mogę się jednak mylić co do tego, musiałbym ponownie przeczytać dokumentację gc, ale raczej jestem pewien, że tak to działa).źródło
__del__
mogą być odśmiecane, jeśli ich liczba referencyjna dla innych obiektów z__del__
jest zero i są one nieosiągalne. Oznacza to, że jeśli masz cykl odniesienia między obiektami__del__
, żaden z nich nie zostanie zebrany. Każdy inny przypadek powinien jednak zostać rozwiązany zgodnie z oczekiwaniami.Lepszą alternatywą jest użycie słabyref.finalizuj . Zobacz przykłady w Finalizer Objects i Porównanie finalizatorów z metodami __del __ () .
źródło
stop()
metodę zamknięcia portów ijoin()
procesów. Jednak jeśli programy nieoczekiwanie wychodzą,stop()
nie jest wywoływane - rozwiązałem to za pomocą finalizatora. Ale w każdym razie_finalizer.detach()
wywołuję metodę stop, aby zapobiec podwójnemu wywołaniu (ręcznie i później przez finalizator).Myślę, że problem może występować,
__init__
jeśli jest więcej kodów niż pokazano?__del__
zostanie wywołany, nawet jeśli__init__
nie został poprawnie wykonany lub nie zgłosił wyjątku.Źródło
źródło
__del__
jest jawne zadeklarowanie wszystkich członków na poziomie klasy, upewniając się, że zawsze istnieją, nawet jeśli__init__
zawiodą. W podanym przykładziefiles = ()
działałoby, choć w większości wystarczyłoby przypisaćNone
; w obu przypadkach nadal musisz przypisać rzeczywistą wartość w__init__
.Oto minimalny działający szkielet:
Ważne: zwróć siebie
Jeśli jesteś podobny do mnie i przeoczysz tę
return self
część ( poprawnej odpowiedzi Clinta Millera ), będziesz patrzył na ten nonsens:Mam nadzieję, że to pomoże następnej osobie.
źródło
Po prostu zawiń destruktor instrukcją try / wyjątek, a nie wyrzuci on wyjątku, jeśli twoje globały są już usunięte.
Edytować
Spróbuj tego:
Wypełni listę plików w funkcji del, która z pewnością istnieje w momencie wywołania. Słaby serwer proxy ma uniemożliwić Pythonowi lub tobie usunięcie zmiennej self.files w jakiś sposób (jeśli zostanie ona usunięta, nie wpłynie to na oryginalną listę plików). Jeśli nie jest tak, że jest on usuwany, mimo że istnieje więcej odwołań do zmiennej, możesz usunąć enkapsulację proxy.
źródło
Wydaje się, że idiomatycznym sposobem na to jest podanie
close()
metody (lub podobnej) i wyraźne jej wywołanie.źródło