Lekcje modułu (3/4)
Metody specjalne (dunder)
Metody specjalne (nazywane metodami dunder ponieważ zaczynają się i kończą
podwójnym znakiem podkreślenia: __name__) to mechanizmy pozwalające integrować
Twoje obiekty z samym językiem: reprezentacja tekstowa, porównywanie za pomocą ==,
obsługa funkcji len(), operatora in, operatorów matematycznych itp.
Znasz już jedną z nich: __init__.
__str__ oraz __repr__
Kontrolują to, co jest wyświetlane przy użyciu print() oraz w konsoli REPL.
class Punto:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Punto({self.x}, {self.y})"
def __str__(self):
return f"({self.x}, {self.y})"
p = Punto(3, 4)
str(p) # '(3, 4)' — dla ludzi
repr(p) # 'Punto(3, 4)' — dla debuggera / programisty
print(p) # '(3, 4)'
p # Punto(3, 4) w konsoli REPL → wywołuje repr
[p, p] # [Punto(3, 4), Punto(3, 4)] — w kontenerach zawsze używa reprJeśli zdefiniujesz tylko __repr__, Python użyje jej również jako alternatywy dla
__str__.
__eq__: redefiniowanie operatora ==
Domyślnie operator == porównuje tożsamość obiektów (is), a nie
ich zawartość. Do porównania strukturalnego zdefiniuj metodę __eq__:
class Punto:
def __init__(self, x, y):
self.x, self.y = x, y
def __eq__(self, other):
if not isinstance(other, Punto):
return NotImplemented
return (self.x, self.y) == (other.x, other.y)
Punto(1, 2) == Punto(1, 2) # True
Punto(1, 2) == Punto(3, 4) # FalseZwrócenie wartości NotImplemented (specjalne słowo kluczowe, a nie False!), gdy typy
nie są porównywalne, jest najlepszą praktyką: Python spróbuje wtedy użyć metody
__eq__ drugiego operandu.
__len__: obsługa funkcji len()
class Cesto:
def __init__(self):
self.frutti = []
def __len__(self):
return len(self.frutti)
c = Cesto()
c.frutti.append("mela")
len(c) # 1
bool(c) # True (len > 0)Definiując __len__, otrzymujesz również zachowanie boolean za darmo: obiekt
z len() == 0 będzie traktowany jako fałsz (falsy).
Inne przydatne metody specjalne (przegląd)
__add__(self, other)→ redefiniuje operator+__lt__,__le__,__gt__,__ge__→ operatory porównania__contains__(self, item)→ obsługuje warunekitem in self__iter__+__next__→ sprawia, że obiekt jest iterowalny__getitem__(self, key)→ pozwala na zapisself[key]__hash__→ wymagana, by obiekt mógł być kluczem w słowniku lub elementem zbioru
Spróbuj sam
Zdefiniuj klasę `Money` z konstruktorem __init__(self, amount, currency) oraz metodą __str__ zwracającą `f'{amount} {currency}'`. Utwórz `s = Money(42, 'EUR')` i oceń `str(s)`.
Pokaż wskazówkę
Zastosuj f-string w metodzie __str__.
Rozwiązanie dostępne po 3 próbach
Ćwiczenie powtórzeniowe
Zdefiniuj klasę `Pair` z konstruktorem __init__(self, a, b) oraz metodą __eq__ porównującą strukturę (a, b). Oceń `Pair(1, 2) == Pair(1, 2)`.
Pokaż wskazówkę
Porównaj krotki (self.a, self.b) == (other.a, other.b).
Rozwiązanie dostępne po 3 próbach
Dodatkowe wyzwanie
Zdefiniuj klasę `Book` z konstruktorem przyjmującym `title` i `author`. Zdefiniuj metodę specjalną `__str__(self)` tak, aby zwracała ciąg znaków `"Title by Author"` (np. `'1984 by George Orwell'`). Utwórz obiekt książki i oceń `str(book)`.
Pokaż wskazówkę
Użyj zapisu f"{self.title} by {self.author}" wewnątrz def __str__(self):.
Rozwiązanie dostępne po 3 próbach