Dziedziczenie
Budowanie klasy na bazie innej: klasa pochodna przejmuje metody klasy bazowej i może je rozszerzać lub nadpisywać.
Przykład
#class User:
def __init__(self, name):
self.name = name
def greet(self):
return f"Cześć, jestem {self.name}"
def role(self):
return "użytkownik"
class Mentor(User):
def role(self):
return "mentor"
def review(self, task):
return f"{self.name} sprawdza zadanie {task}"
darwin = Mentor("Darwin")
print(darwin.greet())
print(darwin.role())
print(darwin.review("7_3"))
print(isinstance(darwin, User), issubclass(Mentor, User))Definicja i zastosowanie
#Dziedziczenie pozwala utworzyć nową klasę na podstawie istniejącej. Klasę bazową podajesz w nawiasie po nazwie: class Mentor(User). Klasa pochodna automatycznie dostaje wszystkie metody i atrybuty klasy bazowej, więc piszesz w niej tylko to, co nowe albo inne.
Metoda o tej samej nazwie w klasie pochodnej nadpisuje wersję bazową. Python szuka metody najpierw w klasie obiektu, potem w jej klasie bazowej i tak dalej, aż do wbudowanej klasy object, po której dziedziczy każda klasa. Tę kolejność (MRO, od method resolution order) pokazuje atrybut __mro__. Z nadpisanej metody wersję bazową wywołasz przez super().
Funkcja isinstance(obiekt, Klasa) zwraca True także dla obiektów klas pochodnych, a issubclass(A, B) sprawdza relację między samymi klasami. Dzięki temu kod napisany dla klasy bazowej działa z każdą klasą pochodną, a każda z nich może na swój sposób reagować na to samo wywołanie. To zjawisko nazywa się polimorfizmem.
Klasa może dziedziczyć po kilku klasach naraz (class Quiz(Timed, Scored)), co przydaje się przy małych klasach dodających jedną umiejętność, tzw. mixinach. Dziedziczenia używaj, gdy zachodzi relacja „jest”: mentor jest użytkownikiem. Gdy obiekt tylko „ma” inny obiekt, np. kurs ma listę lekcji, zapisz go jako atrybut. Takie podejście nazywa się kompozycją.
Składnia
#class Bazowa:
...
class Pochodna(Bazowa):
...
class Pochodna(Bazowa1, Bazowa2):
...Więcej przykładów
#class Task:
def __init__(self, exp):
self.exp = exp
def reward(self):
return self.exp
class BonusTask(Task):
def reward(self):
return self.exp * 2
class Tutorial(Task):
def reward(self):
return 0
tasks = [Task(10), BonusTask(10), Tutorial(10)]
print([task.reward() for task in tasks])
print(sum(task.reward() for task in tasks))class Exercise:
def describe(self):
return "zadanie"
class Timed(Exercise):
def describe(self):
return "zadanie na czas"
class Scored(Exercise):
def describe(self):
return "zadanie za punkty"
class Quiz(Timed, Scored):
pass
print(Quiz().describe())
print([cls.__name__ for cls in Quiz.__mro__])class CodeWorldsError(Exception):
pass
class NoFuelError(CodeWorldsError):
pass
class LockedWorldError(CodeWorldsError):
pass
for error in [NoFuelError("Brak paliwa"), LockedWorldError("Świat 9 jest zablokowany")]:
try:
raise error
except CodeWorldsError as caught:
print(type(caught).__name__, "->", caught)Dobre praktyki
#- Dziedzicz tylko wtedy, gdy obiekt klasy pochodnej naprawdę „jest” obiektem klasy bazowej. Jeśli jedynie z niego korzysta, zapisz go jako atrybut.
- Unikaj głębokich hierarchii. Dwa, trzy poziomy zwykle wystarczą, a każdy kolejny utrudnia ustalenie, skąd pochodzi dana metoda.
- Własne wyjątki wyprowadzaj ze wspólnej klasy bazowej. Jeden
exceptna klasę bazową obsłuży wtedy całą rodzinę błędów.
Powiązane hasła
#- classDefiniuje klasę: szablon obiektów łączący dane i funkcje, które na nich działają.
- super()Wywołuje metodę klasy bazowej z wnętrza klasy pochodnej, najczęściej konstruktor __init__.
- isinstance()Sprawdza, czy obiekt jest danego typu lub jego podtypu.
- __init__()Metoda wywoływana automatycznie przy tworzeniu obiektu. Zapisuje w nim dane początkowe, czyli atrybuty.
- raiseZgłasza wyjątek, sygnalizując błąd lub niedozwoloną sytuację.
Widzisz błąd albo brakuje przykładu? Napisz do nas.