2009-04-15 7 views
100

czytałem Trybików Into Python "oraz w rozdziale dotyczącym klas daje ten przykład:Dziedziczenie i przesłanianie __init__ w Pythonie

class FileInfo(UserDict): 
    "store file metadata" 
    def __init__(self, filename=None): 
     UserDict.__init__(self) 
     self["name"] = filename 

Następnie autor mówi, że jeśli chcesz, aby zastąpić metodę __init__, ty musi jawnie wywołać rodzica __init__ z poprawnymi parametrami.

  1. Co jeśli ta klasa FileInfo ma więcej niż jedną klasę przodków?
    • Czy muszę jawnie wywoływać wszystkie metody klas przodków "__init__?
  2. Czy muszę to zrobić w przypadku jakiejkolwiek innej metody, którą chcę zastąpić?
+3

Uwaga: Przeciążenie to oddzielna koncepcja od nadpisania. –

+2

tak, dzięki za naprawienie tego, –

Odpowiedz

129

Książka jest nieco przestarzała w odniesieniu do wywoływania klasy subklasy. Jest też trochę przestarzały w odniesieniu do wbudowanych klas podklasy.

Wygląda teraz tak.

class FileInfo(dict): 
    """store file metadata""" 
    def __init__(self, filename=None): 
     super(FileInfo, self).__init__() 
     self["name"] = filename 

Należy zwrócić uwagę na następujące kwestie.

  1. Możemy bezpośrednio podklasy wbudowanych klas, jak dict, list, tuple itp

  2. Funkcja super uchwyty tropienia superklas tej klasy i wywołanie funkcji w nich odpowiednio.

+4

czy powinienem szukać lepszej książki/samouczka? –

+1

Więc w przypadku wielokrotnego dziedziczenia, czy super() śledzi je wszystkie w twoim imieniu? – Dana

+2

Co jest nie tak z Dict .__ init __(), dokładnie? –

3

Jeśli klasa FileInfo ma więcej niż jedną klasę przodków, zdecydowanie powinieneś wywołać wszystkie ich funkcje __init __(). Powinieneś także zrobić to samo dla funkcji __del __(), która jest destruktorem.

2

Tak, należy zadzwonić pod numer __init__ dla każdej klasy nadrzędnej. To samo dotyczy funkcji, jeśli nadpisujesz funkcję, która istnieje w obu rodzicach.

10

naprawdę nie mieć zadzwonić do __init__ metod klasy bazowej (ES), ale zazwyczaj chcą to zrobić, ponieważ klasy bazowe zrobi kilka ważnych inicjacji tam, że są potrzebne dla reszty metody klas do pracy.

Dla innych metod zależy to od twoich intencji. Jeśli chcesz dodać coś do zachowania klas bazowych, będziesz chciał wywołać metodę klas bazowych dodatkowo do własnego kodu. Jeśli chcesz zasadniczo zmienić zachowanie, możesz nie wywoływać metody klasy podstawowej i implementować wszystkie funkcje bezpośrednio w klasie pochodnej.

+4

Dla technicznej kompletności, niektóre klasy, takie jak wątki.Thread, będą rzucać gigantyczne błędy, jeśli kiedykolwiek spróbujesz uniknąć wywoływania __init__ rodzica. –

+5

Uważam, że to wszystko "nie musisz wywoływać konstruktora bazy" jest bardzo irytujące. Nie musisz go wywoływać w jakimkolwiek języku, jaki znam. Wszystkie z nich będą zepsuć (lub nie) w podobny sposób, nie inicjując członków. Sugestia, aby nie inicjować klas bazowych jest po prostu błędna na tak wiele sposobów. Jeśli klasa nie potrzebuje inicjalizacji, teraz będzie jej potrzebować w przyszłości. Konstruktor jest częścią interfejsu struktury klas/konstrukcji językowej i powinien być używany poprawnie. Prawidłowe użycie to wywołanie go w konstruktorze pochodnym. Więc zrób to. – AndreasT

17

W każdej klasie, z której należy dziedziczyć, można uruchomić pętlę każdej klasy wymagającej init'd po rozpoczęciu klasy podrzędnej ... przykład, który można skopiować, może być lepiej zrozumiany ...

class Female_Grandparent: 
    def __init__(self): 
     self.grandma_name = 'Grandma' 

class Male_Grandparent: 
    def __init__(self): 
     self.grandpa_name = 'Grandpa' 

class Parent(Female_Grandparent, Male_Grandparent): 
    def __init__(self): 
     Female_Grandparent.__init__(self) 
     Male_Grandparent.__init__(self) 

     self.parent_name = 'Parent Class' 

class Child(Parent): 
    def __init__(self): 
     Parent.__init__(self) 
#---------------------------------------------------------------------------------------# 
     for cls in Parent.__bases__: # This block grabs the classes of the child 
      cls.__init__(self)  # class (which is named 'Parent' in this case), 
            # and iterates through them, initiating each one. 
            # The result is that each parent, of each child, 
            # is automatically handled upon initiation of the 
            # dependent class. WOOT WOOT! :D 
#---------------------------------------------------------------------------------------# 



g = Female_Grandparent() 
print g.grandma_name 

p = Parent() 
print p.grandma_name 

child = Child() 

print child.grandma_name 
+1

Wygląda na to, że pętla for w 'Child .__ init__' nie jest konieczna. Kiedy usuwam go z przykładu, dziecko wciąż drukuje "Babcia". Czy wtyczka grandparent nie jest obsługiwana przez klasę "Parent"? – Adam

+2

Myślę, że proces init dla rodziców jest już obsługiwany przez proces rodzica, prawda? – johk95