2012-01-18 8 views
14

Wynikając z typu wbudowanego, a także z innej klasy, wydaje się, że konstruktor typu wbudowanego nie wywołuje konstruktora klasy super. Powoduje to, że metody __init__ nie są wywoływane dla typów, które pojawiają się po wbudowaniu w MRO.Python 3 typy wbudowane __init__ nie wywołuje super() .__ init__?

przykład:

class A: 
    def __init__(self, *args, **kwargs): 
     super().__init__(*args, **kwargs) 
     print("A().__init__()") 

class B(list, A): 
    def __init__(self, *args, **kwargs): 
     print("B().__init__() start") 
     super().__init__(*args, **kwargs) 
     print("B().__init__() end") 

if __name__ == '__main__': 
    b = B() 

W tym przykładzie, A .__ init__ nie jest tzw. Gdy B jest zdefiniowane jako class B(A, list) zamiast - przełączanie porządku dziedziczenia - działa zgodnie z zamierzeniem (to jest wywoływane jest A .__ init__).

Ta bardzo subtelna zależność od porządku dziedziczenia wydaje się raczej nieprecyzyjna, czy jest to zamierzone w ten sposób? Oznacza to również, że nigdy nie możesz czerpać z wbudowanych typów w skomplikowanych hierarchiach klasowych, ponieważ nie możesz wiedzieć, gdzie wbudowany kończy się w MRO, gdy ktoś inny wywodzi się z twoich klas (horror konserwacyjny). Czy czegoś brakuje?

Dodatkowe info: Python w wersji 3.1

+0

Python nigdy nie miał automatyczne wywołanie nadklasie '__init__' metod – Marcin

+2

Co by to nazwać' a .__ init__' z? 'list .__ init__' przyjmuje jeden argument i zgłasza błąd, jeśli wystąpi. Nawet gdyby zaakceptował dowolne inne argumenty, odrzuciłby pierwszą, więc to, co zobaczyłoby, byłoby zależne od porządku spadkowego. –

+0

Podobne pytanie: http: // stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-dziedziczenie Z podejściem Guido wynikającym z odpowiedzi na to pytanie: http://python-history.blogspot.com/2010/06/method- resolution-order.html – aganders3

Odpowiedz

9

Prawidłowe wykorzystanie super() jest raczej subtelny i wymaga opieki, czy metody współpracując nie wszyscy mają ten sam podpis. Zazwyczaj wzór __init__() metod jest następująca:

class A(object): 
    def __init__(self, param_a, **kwargs): 
     self.param_a = param_a 
     super(A, self).__init__(**kwargs) 

class B(A): 
    def __init__(self, param_b, **kwargs): 
     self.param_b = param_b 
     super(B, self).__init__(**kwargs) 

class C(A): 
    def __init__(self, param_c, **kwargs): 
     self.param_c = param_c 
     super(C, self).__init__(**kwargs) 

class D(B, C): 
    def __init__(self, param_d, **kwargs): 
     self.param_d = param_d 
     super(D, self).__init__(**kwargs) 

d = D(param_a=1, param_b=2, param_c=3, param_d=4) 

Zauważ, że wymaga to, że wszystkie metody współpracy, i że wszystkie metody potrzebują nieco podpisu zgodnego aby upewnić się, że nie ma znaczenia, w którym miejscu metodą jest tzw.

Konstruktory typów wbudowanych nie mają podpisów konstruktorów, które umożliwiają udział w takiej współpracy. Nawet gdyby zadzwonili pod numer super().__init__(), byłoby to raczej bezcelowe, gdyby wszystkie sygnatury konstruktorów nie były zunifikowane. W związku z tym masz rację - nie nadają się do udziału w połączonych wywołaniach konstruktora.

super() może być używany tylko wtedy, gdy wszystkie metody współpracy mają ten sam podpis (np. __setattr__()) lub jeśli używasz powyższego (lub podobnego) wzoru. Używanie super() nie jest jednak jedynym wzorcem do wywoływania metod klasy bazowej. Jeśli w twoim wzorze wielokrotnego dziedziczenia nie ma "diamentów", możesz użyć jawnych wywołań bazowych, na przykład B.__init__(self, param_a). Klasy z wieloma klasami podstawowymi po prostu wywołują wiele konstruktorów. Nawet jeśli istnieją diamenty, możesz czasem używać połączeń jawnych, o ile upewnisz się, że numer __init__() może być wywoływany kilka razy bez szkody.

Jeśli mimo to chcesz używać super() dla contructors, nie powinieneś używać podklas wbudowanych typów (z wyjątkiem object) w wielu hierachiach. Niektóre dalsze czytanie: