Dlaczego metody INIT klasy superclass nie są wywoływane automatycznie?

Dlaczego projektanci Pythona zdecydowali, że metody __init__() podklas nie wywołują automatycznie metod __init__() ich superklas, tak jak w niektórych innych językach? Czy idiom Pythonic i recommended naprawdę jest podobny do poniższego?

class Superclass(object):
    def __init__(self):
        print 'Do something'

class Subclass(Superclass):
    def __init__(self):
        super(Subclass, self).__init__()
        print 'Do something else'
Author: martineau, 2010-09-23

9 answers

Zasadnicza różnica między konstruktorami Pythona __init__ i tymi innymi językami jest taka, że __init__ jesta nie konstruktorem: jest toinicjalizator (rzeczywisty konstruktor (jeśli istnieje, ale zobacz później;-) jest __new__ i działa zupełnie inaczej). Podczas gdy konstruowanie wszystkich superklas (i, bez wątpienia, robiąc to "przed" kontynuowaniem konstruowania w dół) jest oczywiście częścią powiedzenia, że jesteś konstruowaniem instancji podklasy, to jest oczywiście nie dotyczy to inicjalizacji , ponieważ istnieje wiele przypadków użycia, w których inicjalizacja superklas musi być pominięta, zmieniona, kontrolowana-dzieje się, jeśli w ogóle, "w środku" inicjalizacji podklasy, itd.

Zasadniczo, delegacja super-klasy inicjalizatora nie jest automatyczna w Pythonie z dokładnie tych samych powodów, delegacja taka nie jest również automatyczna dla żadnych {9]} innych metod -- i zauważ, że te "inne języki" nie działają automatycznie delegacja superklasowa dla każdej innej metody... po prostu dla konstruktora (i jeśli dotyczy, destruktora), który, jak wspomniałem, jest , a nie tym, czym jest __init__ Pythona. (Zachowanie __new__ jest również dość osobliwe, choć tak naprawdę nie jest bezpośrednio związane z twoim pytaniem, ponieważ __new__ jest tak osobliwym konstruktorem, że niekoniecznie musi cokolwiek konstruować-może idealnie zwrócić istniejącą instancję, a nawet nie-instancję... oczywiście Python oferuje Ci dużo więcej kontroli nad mechaniką niż "inne języki", które masz na myśli, co również obejmuje brak automatycznej delegacji w samym __new__!-).

 168
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-23 22:07:53

Jestem trochę zażenowany, gdy ludzie papugują "Zen Pythona", jakby to było usprawiedliwienie czegokolwiek. Jest to filozofia projektowania; poszczególne decyzje projektowe mogą Zawsze być wyjaśnione bardziej szczegółowo-i muszą być, inaczej "Zen Pythona" stanie się wymówką do robienia czegokolwiek.

Powód jest prosty: niekoniecznie konstruujesz klasę pochodną w sposób podobny do tego, jak konstruujesz klasę bazową. Możesz mieć więcej parametrów, Mniej, mogą być w innej kolejności lub w ogóle nie spokrewnione.

class myFile(object):
    def __init__(self, filename, mode):
        self.f = open(filename, mode)
class readFile(myFile):
    def __init__(self, filename):
        super(readFile, self).__init__(filename, "r")
class tempFile(myFile):
    def __init__(self, mode):
        super(tempFile, self).__init__("/tmp/file", mode)
class wordsFile(myFile):
    def __init__(self, language):
        super(wordsFile, self).__init__("/usr/share/dict/%s" % language, "r")

Dotyczy to wszystkich metod pochodnych, a nie tylko __init__.

 35
Author: Glenn Maynard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-23 22:08:51

Java i C++ wymagają, aby konstruktor klasy bazowej był wywoływany ze względu na układ pamięci.

Jeśli masz klasę BaseClass z członkiem field1 i tworzysz nową klasę SubClass, która dodaje członka field2, wtedy instancja SubClass zawiera spację dla field1 i field2. Do wypełnienia field1 potrzebny jest konstruktor BaseClass, chyba że wszystkie klasy dziedziczące muszą powtarzać inicjalizację BaseClass w ich własnych konstruktorach. A jeśli {[1] } jest prywatne, to dziedziczenie klas nie można zainicjalizować field1.

Python nie jest Javą ani C++. Wszystkie instancje wszystkich klas zdefiniowanych przez Użytkownika mają ten sam 'kształt'. Są to w zasadzie tylko słowniki, w których można wstawiać atrybuty. Zanim zostanie przeprowadzona inicjalizacja, wszystkie instancje wszystkich klas zdefiniowanych przez użytkownika są prawie dokładnie takie same; są to po prostu miejsca do przechowywania atrybutów, które jeszcze nie przechowują żadnych.

Więc to ma sens, aby podklasa Pythona nie wywoływała swojej klasy bazowej konstruktor. Może po prostu dodać same atrybuty, jeśli chce. Nie ma miejsca zarezerwowanego dla danej liczby pól dla każdej klasy w hierarchii i nie ma różnicy między atrybutem dodanym kodem z metody BaseClass a atrybutem dodanym kodem z metody SubClass.

Jeśli, Jak to jest powszechne, SubClass faktycznie chce mieć wszystkie niezmienniki BaseClass skonfigurowane przed przystąpieniem do własnej personalizacji, to tak, możesz po prostu zadzwonić BaseClass.__init__() (lub użyć super, ale to skomplikowane i ma czasem własne problemy). Ale nie musisz. I możesz to zrobić przed, po, lub z różnymi argumentami. Do diabła, gdybyś chciał, mógłbyś wywołać BaseClass.__init__ z innej metody całkowicie niż __init__; może masz jakąś dziwną leniwą inicjalizację.

Python osiąga tę elastyczność, utrzymując rzeczy proste. Inicjalizujesz obiekty pisząc metodę __init__, która ustawia atrybuty na self. To wszystko. Zachowuje się dokładnie jak metoda, ponieważ jest dokładnie metodą. Nie ma innych dziwnych i nieintuicyjnych zasad dotyczących rzeczy, które muszą być zrobione jako pierwsze, lub rzeczy, które automatycznie się wydarzą, jeśli nie zrobisz innych rzeczy. Jedynym celem, któremu musi służyć, jest hook do wykonania podczas inicjalizacji obiektu, aby ustawić początkowe wartości atrybutów i właśnie to robi. Jeśli chcesz, aby zrobił coś innego, wyraźnie napisz to w swoim kodzie.

 19
Author: Ben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-24 05:20:01

"Explicit jest lepszy niż implicit."To samo rozumowanie wskazuje, że powinniśmy wyraźnie napisać "ja".

Myślę, że w końcu jest to korzyść - czy możesz recytować wszystkie zasady Javy dotyczące wywoływania konstruktorów superklas?

 10
Author: Mike Axiak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-23 21:59:08

W tej chwili mamy dość długą stronę opisującą kolejność rozwiązywania metod w przypadku dziedziczenia wielokrotnego: http://www.python.org/download/releases/2.3/mro/

Gdyby konstruktory były wywoływane automatycznie, potrzebowałbyś innej strony o co najmniej tej samej długości wyjaśniającej kolejność tego zdarzenia. To byłoby piekło...

 8
Author: viraptor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-23 22:12:23

Aby uniknąć nieporozumień, warto wiedzieć, że można wywołać metodę base_class __init__(), jeśli Klasa child_class nie ma klasy __init__().

Przykład:

class parent:
  def __init__(self, a=1, b=0):
    self.a = a
    self.b = b

class child(parent):
  def me(self):
    pass

p = child(5, 4)
q = child(7)
z= child()

print p.a # prints 5
print q.b # prints 0
print z.a # prints 1

W rzeczywistości MRO w Pythonie będzie szukać __init__() w klasie rodzica, gdy nie może go znaleźć w klasie dzieci. Musisz wywołać konstruktor klasy nadrzędnej bezpośrednio, jeśli masz już metodę __init__() w klasie potomnej.

Na przykład poniższy kod zwróci błąd: rodzic klasowy: def init(self, a= 1, b=0): siebie.a = a siebie.b = B

    class child(parent):
      def __init__(self):
        pass
      def me(self):
        pass

    p = child(5, 4) # Error: constructor gets one argument 3 is provided.
    q = child(7)  # Error: constructor gets one argument 2 is provided.

    z= child()
    print z.a # Error: No attribute named as a can be found.
 8
Author: Keyvanrm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-12 12:41:20

Często podklasa ma dodatkowe parametry, których nie można przekazać do klasy nadrzędnej.

 7
Author: John La Rooy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-23 22:00:32

Może {[0] } jest metodą, którą podklasa musi nadpisać. Czasami podklasy muszą uruchomić funkcję rodzica przed dodaniem kodu specyficznego dla klasy, A innym razem muszą skonfigurować zmienne instancji przed wywołaniem funkcji rodzica. Ponieważ nie ma możliwości, aby Python mógł wiedzieć, kiedy byłoby najbardziej odpowiednie wywołanie tych funkcji, nie powinien zgadywać.

Jeśli one cię nie kołyszą, weź pod uwagę, że __init__ jest tylko kolejną funkcją. Jeżeli dana funkcja była dostuff zamiast tego, czy nadal chcesz, aby Python automatycznie wywoływał odpowiednią funkcję w klasie nadrzędnej?

 3
Author: Kirk Strauser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-23 22:13:15

Uważam, że jedną bardzo ważną kwestią jest to, że przy automatycznym wywołaniu super.__init__(), wypisujesz, według projektu, kiedy ta metoda inicjalizacji jest wywoływana i z jakimi argumentami. unikanie automatycznego wywołania i wymaganie od programisty jawnego wykonania tego wywołania wymaga dużej elastyczności.

W końcu to, że Klasa B jest pochodną klasy A, nie oznacza, że A.__init__() może lub powinna być wywoływana z tymi samymi argumentami co B.__init__(). wykonanie połączenia jawnego oznacza programista może np. zdefiniować {[2] } z zupełnie innymi parametrami, wykonać obliczenia z tymi danymi, wywołać {[1] } z argumentami odpowiednimi dla tej metody, a następnie wykonać postprocessing. ten rodzaj elastyczności byłby niezręczny do osiągnięcia, gdyby A.__init__() wywoływane było z B.__init__() w sposób dorozumiany, albo przed B.__init__() wykonaniem, albo zaraz po nim.

 2
Author: flow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-24 00:17:26