Czy pragniesz rozpocząć swoją przygodę z programowaniem obiektowym w Pythonie? Już dziś masz szansę postawić pierwsze kroki, zagłębiając się w tajniki metody __init__
tego języka.
W niniejszym przewodniku szczegółowo omówimy podstawowe koncepcje klas i obiektów w Pythonie, by następnie przejść do szczegółowej analizy działania metody __init__
.
Po lekturze tego artykułu będziesz w stanie bez trudu odpowiedzieć na poniższe pytania:
- Czym dokładnie są zmienne instancji, inaczej atrybuty instancji?
- Jak metoda
init
ułatwia proces inicjalizacji atrybutów instancji? - W jaki sposób możemy przypisać wartości domyślne atrybutom?
- Jak wykorzystać metody klasowe jako konstruktory do generowania obiektów?
Zaczynajmy zatem!
Klasy i obiekty w Pythonie
Klasy stanowią fundament programowania obiektowego w Pythonie. Dzięki nim możemy tworzyć struktury, w których definiujemy zarówno dane (atrybuty), jak i operacje na nich wykonywane (metody), łącząc w ten sposób informacje z działaniami.
Gdy klasa jest już zdefiniowana, staje się swoistym planem lub szablonem, na podstawie którego generujemy obiekty, czyli jej instancje.
👩🏫 Czas na przykład! Wyobraźmy sobie klasę Pracownik
, gdzie każdy obiekt tej klasy charakteryzuje się następującymi atrybutami:
full_name
: pełne imię i nazwisko pracownika, w formacie „Imię Nazwisko”emp_id
: unikalny identyfikator pracownikadział
: nazwa działu, do którego pracownik przynależydoświadczenie
: liczba lat doświadczenia zawodowego
Co to konkretnie oznacza? 🤔
Każdy pracownik będzie reprezentowany jako odrębna instancja (obiekt) klasy Pracownik
. Każdy z tych obiektów będzie posiadał unikatowe wartości dla atrybutów takich jak full_name
, emp_id
, dział
i doświadczenie
.
Te atrybuty są również znane jako zmienne instancji, a terminy „atrybut” i „zmienna instancji” będziemy stosować zamiennie.
Za chwilę zajmiemy się dodawaniem atrybutów. Na razie stwórzmy klasę Employee
w następujący sposób:
class Employee: pass
Użycie pass
(jako symbolu zastępczego) pozwala nam uniknąć błędów podczas wykonywania skryptu.
Mimo że aktualna wersja klasy Pracownik
nie jest jeszcze funkcjonalna, to wciąż jest poprawną klasą. Możemy zatem tworzyć obiekty (instancje) tej klasy:
employee_1 = Employee() print(employee_1) #Output: <__main__.Employee object at 0x00FEE7F0>
Możemy również dodać atrybuty i przypisać im wartości, jak pokazano poniżej:
employee_1.full_name="Amy Bell" employee_1.department="HR"
Jednak takie podejście do dodawania atrybutów instancji jest nie tylko mało efektywne, ale również narażone na błędy. Ponadto, nie pozwala ono na pełne wykorzystanie klasy jako szablonu do tworzenia obiektów. W tym miejscu do akcji wkracza metoda __init__
.
Zrozumienie roli metody __init__
w klasie Pythona
Naszym celem jest możliwość inicjalizacji zmiennych instancji już w momencie tworzenia obiektu. Metoda __init__
idealnie się do tego nadaje. Jest ona automatycznie wywoływana za każdym razem, gdy tworzymy nowy obiekt danej klasy, umożliwiając tym samym ustawienie początkowych wartości jego zmiennych.
Dla osób znających C++, metoda __init__
działa bardzo podobnie do konstruktorów w tym języku.
Definiowanie metody __init__
Dodajmy teraz metodę __init__
do naszej klasy Pracownik
:
class Employee: def __init__(self, full_name,emp_id,department,experience): self.full_name = full_name self.emp_id = emp_id self.department = department self.experience = experience
Parametr self
odnosi się do konkretnej instancji klasy, a zapis self.attribute
inicjalizuje atrybut instancji wartością z prawej strony.
Od teraz możemy tworzyć obiekty w następujący sposób:
employee_2 = Employee('Bella Joy','M007','Marketing',3) print(employee_2) # Output: <__main__.Employee object at 0x017F88B0>
Jak widać, wypisanie obiektu nie daje zbyt wielu użytecznych informacji poza tym, do jakiej klasy on należy. Dodajmy zatem metodę __repr__
, która zdefiniuje reprezentację tekstową klasy:
def __repr__(self): return f"{self.full_name},{self.emp_id} z działu {self.department} z {self.experience} latami doświadczenia."
Po dodaniu __repr__
do klasy Pracownik
otrzymujemy:
class Employee: def __init__(self, full_name,emp_id,department,experience): self.full_name = full_name self.emp_id = emp_id self.department = department self.experience = experience def __repr__(self): return f"{self.full_name},{self.emp_id} z działu {self.department} z {self.experience} latami doświadczenia."
Teraz obiekty klasy Pracownik
mają bardziej zrozumiałą reprezentację tekstową:
print(employee_2) # Output: Bella Joy,M007 z działu Marketing z 3 latami doświadczenia.
Kilka konwencji
Zanim przejdziemy dalej, warto zwrócić uwagę na kilka kwestii:
- Użyliśmy
self
jako pierwszego parametru w metodzie__init__
, aby odwołać się do instancji klasy. Następnie użyliśmyself.nazwa_atrybutu
do inicjalizacji atrybutów. Używanieself
jest powszechnie przyjętą konwencją (można użyć dowolnej innej nazwy, ale nie jest to zalecane). - Podczas definiowania metody
__init__
, nazwy parametrów odpowiadają nazwom atrybutów. Podnosi to czytelność kodu.
Jak dodać domyślne wartości atrybutów
W przykładzie, który stworzyliśmy do tej pory, wszystkie atrybuty były wymagane. Oznacza to, że utworzenie obiektu jest możliwe tylko wtedy, gdy przekażemy wartości dla wszystkich pól konstruktora.
Spróbujmy utworzyć instancję klasy Pracownik
bez podawania wartości dla atrybutu doświadczenie
:
employee_3 = Employee('Jake Lee','E001','Engineering')
Otrzymamy następujący błąd:
Traceback (most recent call last): File "main.py", line 22, in <module> employee_3 = Employee('Jake Lee','E001','Engineering') TypeError: __init__() missing 1 required positional argument: 'experience'
Jeżeli jednak chcemy, aby niektóre atrybuty były opcjonalne, możemy to osiągnąć przez przypisanie im domyślnych wartości podczas definiowania metody __init__
.
W naszym przypadku, przypiszmy domyślną wartość 0 dla atrybutu doświadczenie
:
class Employee: def __init__(self, full_name,emp_id,department,experience=0): self.full_name = full_name self.emp_id = emp_id self.department = department self.experience = experience def __repr__(self): return f"{self.full_name},{self.emp_id} z działu {self.department} z {self.experience} latami doświadczenia."
Teraz obiekt pracownik_3
zostanie utworzony bez wartości dla atrybutu doświadczenie
, a jego domyślna wartość wyniesie 0:
employee_3 = Employee('Jake Lee','E001','Engineering') print(employee_3.experience) # Output: 0
Alternatywne konstruktory klas z wykorzystaniem metod klasowych
Do tej pory nauczyliśmy się, jak definiować metodę __init__
oraz ustawiać domyślne wartości atrybutów. Wiemy również, że musimy przekazać wartości wymaganych atrybutów konstruktorowi.
Czasami jednak dane, które mają zostać przypisane jako wartości zmiennych instancji (atrybutów), mogą być dostępne w innej formie, np. jako krotka, słownik lub łańcuch JSON.
Co w takiej sytuacji powinniśmy zrobić?
Rozważmy przykład. Załóżmy, że wartości zmiennych instancji mamy dostępne w słowniku Pythona:
dict_fanny = {'name':'Fanny Walker','id':'H203','dept':'HR','exp':2}
Możemy oczywiście wyciągnąć wszystkie wartości z tego słownika w ten sposób:
name = dict_fanny['name'] id = dict_fanny['id'] dept = dict_fanny['dept'] exp = dict_fanny['exp']
Następnie możemy utworzyć obiekt, przekazując te wartości do konstruktora klasy:
employee_4 = Employee(name, id, dept, exp) print(employee_4) # Output: Fanny Walker,H203 z działu HR z 2 latami doświadczenia.
Jednak to rozwiązanie nie jest optymalne, gdyż musielibyśmy wykonywać te same czynności dla każdego tworzonego obiektu. Istnieje bardziej efektywny sposób! Ale jak to zrobić?
W Pythonie możemy używać metod klasowych jako konstruktorów obiektów. Aby utworzyć metodę klasową, używamy dekoratora @classmethod
.
Zdefiniujmy metodę, która analizuje słownik, pobiera wartości zmiennych instancji i wykorzystuje je do tworzenia obiektów pracownika.
@classmethod def from_dict(cls,data_dict): full_name = data_dict['name'] emp_id = data_dict['id'] department = data_dict['dept'] experience = data_dict['exp'] return cls(full_name, emp_id, department, experience)
Od teraz, gdy będziemy chcieli utworzyć obiekt na podstawie danych ze słownika, możemy użyć metody klasowej from_dict()
.
💡 Zauważ, że w metodzie klasowej używamy cls
zamiast self
. Tak jak self
odnosi się do instancji, tak cls
odnosi się do klasy. Ponadto, metody klasowe są powiązane z klasą, a nie z jej obiektami.
Gdy wywołujemy metodę klasową from_dict()
w celu utworzenia obiektów, wywołujemy ją na klasie Employee
:
emp_dict = {'name':'Tia Bell','id':'S270','dept':'Sales','exp':3} employee_5 = Employee.from_dict(emp_dict) print(employee_5) # Output: Tia Bell,S270 z działu Sales z 3 latami doświadczenia.
Teraz, mając słownik dla każdego z n pracowników, możemy skorzystać z metody klasowej from_dict()
jako konstruktora do tworzenia obiektów, bez konieczności ręcznego wyciągania wartości zmiennych ze słownika.
📝 Uwaga o zmiennych klasowych
Zdefiniowaliśmy metodę klasową, która jest powiązana z klasą, a nie z konkretnymi instancjami. Podobnie jak w przypadku metod klasowych, możemy również definiować zmienne klasowe.
Zmienne klasowe są powiązane z klasą, a nie z jej instancjami. Gdy dany atrybut ma stałą wartość dla wszystkich obiektów klasy, warto zdefiniować go jako zmienną klasową.
Najczęściej zadawane pytania
1. Dlaczego metoda __init__
jest potrzebna w Pythonie?
Metoda __init__
w definicji klasy umożliwia inicjalizację atrybutów (zmiennych instancji) dla każdego obiektu klasy. Jest ona wywoływana automatycznie przy każdym utworzeniu nowej instancji.
2. Czy w klasie Pythona można zdefiniować wiele metod __init__
?
Teoretycznie, wielokrotne zdefiniowanie metody __init__
w klasie miałoby umożliwić posiadanie wielu konstruktorów. W praktyce, nie można zdefiniować wielu metod __init__
, ponieważ druga i każda kolejna implementacja nadpisuje poprzednią. Jednakże, można użyć dekoratora @classmethod
do zdefiniowania metod klasowych, które działają jak alternatywne konstruktory.
3. Co się stanie, jeśli nie zdefiniujemy metody __init__
w klasie?
Brak metody __init__
nie uniemożliwia tworzenia obiektów. Jednakże będziemy musieli ręcznie dodawać zmienne instancji i przypisywać im wartości dla każdego obiektu. Nie będziemy mogli przekazywać wartości zmiennych instancji w konstruktorze. Takie podejście jest nie tylko podatne na błędy, ale również podważa ideę wykorzystania klasy jako wzorca do tworzenia obiektów.
4. Czy można użyć domyślnych wartości argumentów w metodzie __init__
?
Tak, podczas definiowania metody __init__
można podać wartości domyślne dla wybranych atrybutów. Użycie wartości domyślnych sprawia, że atrybuty stają się opcjonalne. Jeśli podczas tworzenia obiektu nie podamy wartości takiego atrybutu, zostanie użyta wartość domyślna.
5. Czy atrybuty można modyfikować poza metodą __init__
?
Tak, wartość atrybutu można zmieniać poza metodą __init__
. Można również dynamicznie dodawać nowe atrybuty do instancji już po jej utworzeniu.
Podsumowanie
W niniejszym poradniku nauczyliśmy się, jak używać metody __init__
do inicjalizacji wartości zmiennych instancji. Mimo swojej prostoty, w przypadku dużej liczby atrybutów, proces ten może być powtarzalny.
Osobom zainteresowanym, polecam zapoznanie się z modułem dataclasses
. W Pythonie w wersji 3.7 i nowszych, istnieje wbudowany moduł klas danych, które upraszczają proces tworzenia klas przechowujących dane. Poza domyślną implementacją __init__
i innych często wykorzystywanych metod, klasy te posiadają również wiele użytecznych funkcji, m.in. podpowiedzi typów, złożone wartości domyślne i optymalizacje.
Następnie warto dowiedzieć się więcej o konstrukcji if __name__ == '__main__'
w Pythonie.
newsblog.pl