2016-11-14 56 views
7

Czasami wygląda rozsądnie używać __init__ jako metoda inicjalizacji dla już istniejącego obiektu, tj:Czy można __init__ używać jako normalnej metody inicjalizacji, a nie konstruktora?

class A(): 
    def __init__(self, x): 
     self.x = x 

    def set_state_from_file(self, file): 
     x = parse_file(file) 
     self.__init__(x) 

Jako alternatywę dla tej implementacji widzę następujące:

class A(): 
    def __init__(self, x): 
     self.init(x)   

    def init(self, x): 
     self.x = x 

    def set_state_from_file(self, file): 
     x = parse_file(file) 
     self.init(x) 

Wydaje mi jak nad- komplikacja kodu. Czy istnieją jakieś wytyczne dotyczące tej sytuacji?

+1

Dlaczego nie ma funkcji non-member aby analizować ten plik i utworzyć nową instancję? Wygląda na to, że będziesz musiał ponownie napisać klasę za każdym razem, gdy chcesz ją zainicjować z innego źródła, co narusza zasadę [Odpowiedzialności za jedno działanie] (https://en.wikipedia.org/wiki/Single_responsibility_principle). –

+0

@Peter Wood, a co z '__init__' w' __setstate__'? – Sklavit

Odpowiedz

11

__init__ nie jest konstruktorem. Jest to metoda inicjująca, o nazwie po instancja została już zbudowana dla ciebie (rzeczywista metoda konstruktora nazywa się __new__()).

Zawsze możesz ponownie połączyć się z kodem, jeśli chcesz ponownie zainicjować, nie jest to naruszenie stylu. W rzeczywistości jest on używany w standardowej bibliotece Python; zobacz multiprocessing.heap.Heap() implementation na przykład:

def malloc(self, size): 
    # return a block of right size (possibly rounded up) 
    assert 0 <= size < sys.maxsize 
    if os.getpid() != self._lastpid: 
     self.__init__()      # reinitialize after fork 

lub threading.local implementation, która używa menedżera kontekstowe odroczyć inicjalizacji.

Nie ma nic szczególnego w samej metodzie __init__. Jest on automatycznie wywoływany przez type.__call__ (po utworzeniu instancji z instance = cls.__new__(cls, *args, **kwargs), wywoływana jest cls.__init__(instance, *args, **kwargs), jeśli jest dostępna).

4

Oprócz odpowiedzi Martjin za: wspólny wzór w Pythonie jest użycie classmethods jako metod fabrycznych, czyli:

class A(): 
    def __init__(self, x): 
     self.x = x 

    @classmethod 
    def from_file(cls, file): 
     x = parse_file(file) 
     return cls(x) 


a1 = A(42) 
a2 = A.from_file(open("/path/to/file")) 
+0

----------------------------------- – Sklavit

+0

@Sklavit wtedy oczywiście wzór "classmethod as alternate constructor" jest nie to, czego potrzebujesz

+0

Co to ma wspólnego z ponownym użyciem metody '__init__' na istniejącej instancji? –

0

znalazłem kilka różnic między __init__ i 'normalnych' metod:

1. , __init__ nie może zwrócić niczego: TypeError zostanie podniesiony.

2., Jeśli __init__ podnosi błąd, __del__ będzie nazwane: UPDATE Martijn Pieters: to jest tylko dla połączeń konstruktora, a nie do użytku generycznego, patrz komentarze poniżej.

class A(object): 
    def __init__(self): 
      print('__init__') 
      raise ValueError('__init__ error') 
      pass 

    def method(self): 
     raise ValueError('method error') 

    def __del__(self): 
     print("__del__") 

def main(): 
    try: 
     a = A() 
     a.method() 
    except ValueError as e: 
     print(e) 
    print('exit main') 

if __name__ == '__main__': 
    main() 
    print('end of file') 

wyjście wola:

__init__ 
__init__ error 
__del__ 
exit main 
end of file 
+0

Tak, oczywiście nowa instancja zostanie wyczyszczona; wyjątek podczas budowy (który obejmuje wywoływanie inicjalizatora '__init__') oznacza, że ​​nowy obiekt nie może zostać zwrócony; wyjątek przerwał normalny przepływ, nie ma przydziału i nie ma żadnych odniesień. Jeśli użyjesz 'inst = A .__ nowy __ (A)' i 'inst .__ init __()' nadal będziesz miał odniesienie do obiektu i '__del__' będzie * nie * zostanie wywołany. Nie ma to nic wspólnego z '__init__', jest to normalna metoda. –

+0

Nie jestem pewien, co to ma wspólnego z ponownym użyciem metody '__init__' na istniejącej instancji. –

+0

Ponadto, 'TypeError' jest generowany przez kod, który wywołuje' __init__'; metoda nie jest specjalna, tylko wywołujący jest (ale nie bardziej, niż inny kod, który używa specjalnych metod, które łamią uzgodnione do zerwania umowy, spróbuj zwrócić coś innego niż ciąg z '__str__' na przykład). Wywołaj metodę '__init__' bezpośrednio i nic się nie stanie, jeśli zwrócisz coś z niej innego niż' Brak'. –