Konstruktor w Pythonie

Zanim zanurzymy się w tematach związanych z tajemniczym konceptem dziedziczenia, zapoznamy się z elementem klasy nazywanym konstruktorem, który będziemy wykorzystywać bardzo często i na różne sposoby. Zapewne do tej pory spotkaliście się już z metodą __init__ (chociażby w lekcji Refaktoryzacja – oczyszczanie kodu – Dwa słowa o instancji i self w Pythonie). Jest to specjalna metoda, dosyć powszechnie nazywana konstruktorem (chociaż poprawniej byłoby ją nazywać metodą inicjalizacyjną), która wywoływana jest jako pierwsza podczas inicjalizacji nowego obiektu. Służy do ustawienia różnych wartości i przygotowania danego obiektu do naszych celów. Od nas zależy co umieścimy w __init__ i czy w ogóle tę metodę zdefiniujemy, gdyż __init__ jest opcjonalny.

Dla lepszego zobrazowania metody __init__ posłużymy się przykładem dwóch prostych klas:

class Human:
   def __init__(self):
       print('New Human was born!')

   def speak(self):
       print('I can speak!')

class Animal:
   def speak(self):
       print('I can not speak!')

# Class usage examples
print("Let's create Adam!")
adam = Human()

print("Let's create a dog!")
dog = Animal()

print('Now all speak!')
adam.speak()
dog.speak()

Pierwsza klasa Human posiada __init__, natomiast klasa Animal nie posiada. Obie konstrukcje są poprawne. Jaki wynik otrzymamy po uruchomieniu skryptu?

Let's create Adam!
New Human was born!
Let's create a dog!
Now all speak!
I can speak!
I can not speak!

Widzimy, że zaraz po inicjalizacji obiektu Adam wykonywana jest metoda __init__. Nie musieliśmy nic więcej robić tylko utworzyć nowy obiekt adam = Human(), i poprzez użycie klasy Human(), __init__ wywołał się automatycznie.

W powyższych przykładach używaliśmy konstruktora bezargumentowego, nazywa on się tak ponieważ, poza niezbędnym self nie wymagamy innych argumentów w implementacji metody __init__ czyli: def __init__(self). Warto zanaczyć, że jeśli w __init__ nie ma argumentów, to przy użyciu naszej klasy, też nie będziemy ich potrzebować, czyli adam = Human(). Możemy to nazwać bezargumentową klasą inicjalizującą).
Jak wygląda przekazywanie argumentów i definiowanie parametrów? Identycznie jak w przypadku metod w klasie – wystarczy dopisać parametry w nawiasie 😉 W poniższym przykładzie dodajemy parametr name a następnie przypisujemy go do zmiennej z prefiksem self co sprawi, że wszystkie metody w klasie będą miały do niej dostęp. Skorzystamy z tego i dodamy tę zmienną do tekstu wypisywanego na konsolę:

class Human:
   def __init__(self, name):
       self.name = name
       print(f'New Human was born! His name is {self.name}!')

   def speak(self):
       print(f'I can speak! My name is {self.name}!')

class Animal:
   def speak(self):
       print('I can not speak!')

print("Let's create Adam!")
adam = Human('Adam')
print("Let's create a dog!")
dog = Animal()
print('Now all speak!')
adam.speak()
dog.speak()

Po uruchomieniu dostaniemy następujący wynik:

Let's create Adam!
New Human was born! His name is Adam!
Let's create a dog!
Now all speak!
I can speak! My name is Adam!
I can not speak!

Tutaj może pojawić się pytanie

Co się stanie gdy spróbujemy stworzyć obiekt, którego konstruktor przyjmuje jeden parametr (tak jak u nas name), bez podawania wartości tego parametru?

Sprawdźmy na poniższym przykładzie:

class Human:
   def __init__(self, name):
       self.name = name
       print(f'New Human was born! His name is {self.name}!')

   def speak(self):
       print(f'I can speak! My name is {self.name}!')


print("Let's create Adam!")
adam = Human()

Jaki będzie wynik po uruchomieniu?

Let's create Adam!
Traceback (most recent call last):
  File "C:/Users/jaktestowac/.PyCharmCE2018.2/config/scratches/init_fun_2.py", line 10, in 
    adam = Human()
TypeError: __init__() missing 1 required positional argument: 'name'

Dostajemy wyraźną informację o błędzie, a dokładniej o braku wartości dla parametru name. Czyli możemy zauważyć, że dodanie parametru do metody inicjalizującej tj konstruktora powoduje, że ten parametr jest wymagany przy korzystaniu z naszej klasy czyli Human(name).)

Na koniec warto wspomnieć jeszcze o dwóch rzeczach:

  • metoda __init__ nie może nic zwracać za pomocą wyrażenia return. Dla poniższego kodu:
    class Human:
       def __init__(self, name):
           self.name = name
           print(f'New Human was born! His name is {self.name}!')
           return name
    
       def speak(self):
           print(f'I can speak! My name is {self.name}!')
    
    
    print("Let's create Adam")
    adam = Human("Adam")
    

    dostaniemy błąd, informujący nas, że metoda __init__ nie powinna zawierać wyrażenia return:

    Let's create Adam!
    Traceback (most recent call last):
    New Human was born! His name is Adam!
      File "C:/Users/jaktestowac/.PyCharmCE2018.2/config/scratches/init_fun_2.py", line 11, in 
        adam = Human('Adam')
    TypeError: __init__() should return None, not 'str'
    
  • z racji, że metoda __init__ jest częścią klasy i związana jest z daną instancją obiektu, to zawsze jej pierwszym parametrem będzie self, czyli def __init__(self):.

Teraz, gdy już znamy podstawy o konstruktorze, możemy przenieść się do głównego tematu tej sekcji – dziedziczenia.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *