2013-05-02 9 views
5

Załóżmy, że mam naprawdę dużą klasę Pythona, która może zużywać sporą ilość pamięci. Klasa ma jakąś metodę, która jest odpowiedzialna za sprzątanie pewne rzeczy, gdy interpreter wychodzi i robi zarejestrowany z modułem atexit:Jakie są implikacje rejestracji metody instancji w atexit w Pythonie?

import atexit 
import os 

class ReallyBigClass(object): 
    def __init__(self, cache_file): 
     self.cache_file = open(cache_file) 
     self.data = <some large chunk of data> 
     atexit.register(self.cleanup) 

    <insert other methods for manipulating self.data> 

    def cleanup(self): 
     os.remove(self.cache_file) 

różne instancje tej klasy może przychodzą i odchodzą przez cały okres trwania programu. Moje pytania to:

Czy rejestracja metody instancji z atexit jest bezpieczna, jeśli, na przykład, del wszystkie moje inne odniesienia do instancji? Innymi słowy, czy atexit.register() zwiększa licznik referencyjny w taki sam sposób jak tradycyjne wiązanie? Jeśli tak, to czy cała instancja klasy musi się teraz zawiesić w pamięci i czekać do końca, ponieważ jedna z jej metod została zarejestrowana z atrybutem atexit, lub czy fragmenty instancji mogą zostać usunięte? Jaki byłby preferowany sposób struktury takiego czyszczenia przy wyjściu dla tymczasowych instancji klasy, tak aby odśmiecanie mogło się odbywać skutecznie?

Odpowiedz

7

Zarejestrowanie metody instancji za pomocą atexit powoduje, że całe wystąpienie klasy będzie trwało do czasu zakończenia tłumaczenia. Rozwiązaniem jest odłączenie wszystkich funkcji zarejestrowanych w klasie od atexit. Następnie można pomyślnie odzyskać instancje. Na przykład,

import atexit 
import os 
import gc 
import random 

class BigClass1(object): 
    """atexit function tied to instance method""" 
    def __init__(self, cache_filename): 
     self.cache_filename = cache_filename 
     self.cache_file = open(cache_filename, 'wb') 
     self.data = [random.random() for i in range(10000000)] 
     atexit.register(self.cleanup) 

    def cleanup(self): 
     self.cache_file.close() 
     os.remove(self.cache_filename) 

class BigClass2(object): 
    def __init__(self, cache_filename): 
     """atexit function decoupled from instance""" 
     self.cache_filename = cache_filename 
     cache_file = open(cache_filename, 'wb') 
     self.cache_file = cache_file 
     self.data = [random.random() for i in range(10000000)] 

     def cleanup(): 
      cache_file.close() 
      os.remove(cache_filename) 

     atexit.register(cleanup) 

if __name__ == "__main__": 
    import pdb; pdb.set_trace() 

    big_data1 = BigClass1('cache_file1') 
    del big_data1 
    gc.collect() 

    big_data2 = BigClass2('cache_file2') 
    del big_data2 
    gc.collect() 

Krocząc przez tę linię po linii i monitorowanie procesu pokazuje, że pamięć pamięć spożywane przez big_data1 odbywa się aż do zjazdu tłumacza podczas big_data2 jest z powodzeniem śmieci zebrane po del. Uruchamianie każdego przypadku testowego (skomentuj inny przypadek testowy) zapewnia takie same wyniki.

0

Moja wyobraźnia dla atexit realizacji jest jak poniżej:

try: 
    # Your whole program 
finally: 
    if sys.exitfunc: 
     sys.exitfunc() 

i moduł atexit tylko setup sys.exitfunc własnego zwrotnego. Nie musisz więc martwić się o żadne zniknięte obiekty z powodu jakiegokolwiek czyszczenia spowodowanego przez wyłączenie interpretera.

UWAGA 1: nie jest określony, co się stanie, jeśli sys.exitfunc wezwie sys.exit(EXIT_CODE) ale w moim przypadku „EXIT_CODE” jest zwrócony

UWAGA 2: atexit wykonuje wszystkie funkcje zarejestrowanych kolejno i złapać wszystkie wyjątki, dlatego też chowaj wykonanie sys.exit (ponieważ tylko powoduje to wyjątek SystemExit)

Powiązane problemy