Powrót do: Podstawy Testów Automatycznych w Selenium i Python cz. 5 – Profesjonalna konfiguracja projektu
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, inadam = 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żeniareturn
. 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żeniareturn
: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ędzieself
, czylidef __init__(self):
.
Teraz, gdy już znamy podstawy o konstruktorze, możemy przenieść się do głównego tematu tej sekcji – dziedziczenia.