Przeciążanie operatorów
Część sekcji Programowanie obiektowe ścieżki Python w Coddy. Lekcja 34 z 64.
Przeciążanie operatorów pozwala Twoim klasom współpracować z wbudowanymi operatorami Pythona (+, -, *, itd.) poprzez implementację specjalnych metod magicznych.
Oto przykład klasy z przeciążaniem operatorów:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def __str__(self):
return f"Vector({self.x}, {self.y})"Metoda __add__ określa, co się dzieje, gdy używasz operatora +:
v1 = Vector(2, 3)
v2 = Vector(5, 7)
result = v1 + v2 # Wywołuje v1.__add__(v2)
print(result)Metoda __mul__ określa, co się dzieje, gdy używasz operatora *:
v1 = Vector(2, 3)
scaled = v1 * 3 # Wywołuje v1.__mul__(3)
print(scaled)Wynik:
Vector(7, 10)
Vector(6, 9)Dodaj operatory porównania:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __str__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(1, 1)
print(v1 == v2) # True - wywołuje v1.__eq__(v2)
print(v1 == v3) # FalseKluczowa kwestia: Przeciążanie operatorów wykorzystuje metody specjalne, takie jak __add__ (+), __sub__ (-), __mul__ (*), __eq__ (==), aby określić, jak operatory działają z twoimi obiektami. Dzięki temu twoje klasy naturalnie współpracują z wbudowanymi operatorami Pythona.
Wyzwanie
ŚredniW tym wyzwaniu zaimplementujesz klasę Money, która reprezentuje kwoty pieniężne i obsługuje przeciążanie operatorów. Twoja implementacja zostanie dokładnie sprawdzona za pomocą kompleksowego zestawu testów.
money.py— to jedyny plik, który musisz edytować. Zawiera definicję klasy z komentarzami TODO wskazującymi, co należy zaimplementować.driver.py— zawiera rozbudowane scenariusze testowe, które sprawdzają Twoją implementację (nie modyfikuj tego pliku).
Zaimplementuj klasę Money z następującymi funkcjami:
- Konstruktor przyjmujący
amount(float) icurrency(string) - Dodawanie (
+) obiektów Money o tej samej walucie - Mnożenie (
*) przez liczbę w celu przeskalowania kwoty - Porównywanie równości (
==) obiektów Money - Reprezentacja tekstowa w formacie „X.XX CUR”
Spróbuj swoich sił
from money import Money
# Obsługa przypadku testowego
test_case = input()
def test_basic_functionality():
# Test inicjalizacji i reprezentacji tekstowej
m1 = Money(10.0, "USD")
assert str(m1) == "10.00 USD", f"__str__ method failed, got {str(m1)}"
# Test dodawania
m2 = Money(20.0, "USD")
m3 = m1 + m2
assert str(m3) == "30.00 USD", f"Addition failed, got {str(m3)}"
# Test dodawania kwot w różnych walutach
m4 = Money(20.0, "EUR")
try:
m5 = m1 + m4
assert False, "Adding different currencies should raise an error"
except ValueError as e:
assert str(e) == "Cannot add different currencies", f"Wrong error message: {str(e)}"
# Test mnożenia
m6 = m1 * 3
assert str(m6) == "30.00 USD", f"Multiplication failed, got {str(m6)}"
# Test równości
assert m1 == Money(10.0, "USD"), "Equality test failed"
assert m1 != m2, "Inequality test failed"
print("All basic functionality tests passed!")
def test_zero_values():
# Test z kwotą zerową
m1 = Money(0.0, "USD")
assert str(m1) == "0.00 USD", f"Zero amount string representation failed, got {str(m1)}"
# Test dodawania zera
m2 = Money(10.0, "USD")
m3 = m1 + m2
assert str(m3) == "10.00 USD", f"Addition with zero failed, got {str(m3)}"
# Test mnożenia przez zero
m4 = m2 * 0
assert str(m4) == "0.00 USD", f"Multiplication by zero failed, got {str(m4)}"
# Test równości dla kwoty zerowej
assert m1 == Money(0.0, "USD"), "Equality with zero amount failed"
assert m1 != m2, "Inequality with zero amount failed"
print("All zero value tests passed!")
def test_negative_values():
# Test z kwotą ujemną
m1 = Money(-10.0, "USD")
assert str(m1) == "-10.00 USD", f"Negative amount string representation failed, got {str(m1)}"
# Test dodawania kwot ujemnych
m2 = Money(20.0, "USD")
m3 = m1 + m2
assert str(m3) == "10.00 USD", f"Addition with negative amount failed, got {str(m3)}"
m4 = Money(-5.0, "USD")
m5 = m1 + m4
assert str(m5) == "-15.00 USD", f"Addition of two negative amounts failed, got {str(m5)}"
# Sprawdź mnożenie przez ujemny skalar
m6 = m2 * -2
assert str(m6) == "-40.00 USD", f"Multiplication by negative scalar failed, got {str(m6)}"
# Sprawdź równość dla ujemnych kwot
assert m1 == Money(-10.0, "USD"), "Equality with negative amount failed"
assert m1 != m2, "Inequality with negative amount failed"
print("All negative value tests passed!")
def test_large_values():
# Sprawdź działanie dla bardzo dużych kwot
m1 = Money(1000000.0, "USD")
assert str(m1) == "1000000.00 USD", f"Large amount string representation failed, got {str(m1)}"
# Sprawdź dodawanie dużych kwot
m2 = Money(2000000.0, "USD")
m3 = m1 + m2
assert str(m3) == "3000000.00 USD", f"Addition with large amounts failed, got {str(m3)}"
# Sprawdź mnożenie przez duży skalar
m4 = m1 * 1000
assert str(m4) == "1000000000.00 USD", f"Multiplication with large scalar failed, got {str(m4)}"
print("All large value tests passed!")
def test_precision():
# Sprawdź działanie dla kwot z częścią ułamkową
m1 = Money(10.25, "USD")
assert str(m1) == "10.25 USD", f"Fractional amount string representation failed, got {str(m1)}"
m2 = Money(10.2, "USD")
assert str(m2) == "10.20 USD", f"Two decimal place formatting failed, got {str(m2)}"
# Sprawdź dodawanie kwot z częścią ułamkową
m3 = Money(0.75, "USD")
m4 = m1 + m3
assert str(m4) == "11.00 USD", f"Addition with fractional amounts failed, got {str(m4)}"
# Sprawdź mnożenie przez skalar z częścią ułamkową
m5 = m1 * 0.5
assert str(m5) == "5.13 USD", f"Multiplication with fractional scalar failed, got {str(m5)}"
print("All precision tests passed!")
def test_type_validation():
try:
# Te operacje powinny działać bez błędów
m1 = Money(10.0, "USD")
m2 = m1 * 2
m3 = m1 * 2.5
# Sprawdź równość dla różnych typów
assert (m1 == "10.00 USD") == False, "Equality with string should return False"
assert (m1 == 10.0) == False, "Equality with number should return False"
print("All type validation tests passed!")
except Exception as e:
print(f"Type validation test failed: {e}")
def test_currency_case_sensitivity():
# Sprawdź rozróżnianie wielkości liter w kodach walut
m1 = Money(10.0, "USD")
m2 = Money(10.0, "usd")
# W kodach walut wielkość liter powinna mieć znaczenie
assert m1 != m2, "Currency comparison should be case-sensitive"
# Dodawanie walut o różnej wielkości liter powinno się nie powieść
try:
m3 = m1 + m2
assert False, "Adding different currency cases should raise an error"
except ValueError as e:
assert str(e) == "Cannot add different currencies", f"Wrong error message: {str(e)}"
print("All currency case sensitivity tests passed!")
def test_performance():
# Utwórz wiele obiektów Money i wykonaj operacje
base = Money(1.0, "USD")
result = base
# Wykonaj 1000 dodawań
for i in range(1000):
result = result + Money(1.0, "USD")
assert str(result) == "1001.00 USD", f"Performance test addition failed, got {str(result)}"
# Wykonaj 10 mnożeń
result = base
for i in range(10):
result = result * 2
assert str(result) == "1024.00 USD", f"Performance test multiplication failed, got {str(result)}"
print("All performance tests passed!")
# Uruchom odpowiedni test na podstawie danych wejściowych
if test_case == "basic_test":
test_basic_functionality()
elif test_case == "zero_values":
test_zero_values()
elif test_case == "negative_values":
test_negative_values()
elif test_case == "large_values":
test_large_values()
elif test_case == "precision":
test_precision()
elif test_case == "type_validation":
test_type_validation()
elif test_case == "currency_case":
test_currency_case_sensitivity()
elif test_case == "performance":
test_performance()
else:
print(f"Unknown test case: {test_case}")Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektyParametr selfMetodyAtrybutyMetoda konstruktora (__init__)Powtórzenie – prosty kalkulator4Dziedziczenie
Podstawy dziedziczeniaFunkcja super()Nadpisywanie metodDziedziczenie wielokrotneKolejność rozwiązywania metodPodsumowanie – hierarchia pracowników7Metody specjalne
Wprowadzenie do metod magicznychPrzeciążanie operatorówMagiczne metody kontenerówPowtórka – własna lista10Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy2Dekoratory
Wprowadzenie do dekoratorówDekorator propertyDekorator metody statycznejDekorator metody klasowej5Polimorfizm
Ponowne omówienie nadpisywania metodTypowanie kaczeKlasy i metody abstrakcyjneProjektowanie interfejsówPodsumowanie — kalkulator kształtów8Zaawansowane koncepcje programowania obiektowego
Kompozycja a dziedziczenieMiksyMetody statyczne i klasoweDekoratory klasMenedżery kontekstu11Wzorce projektowe, część 2
Wzorzec PolecenieWzorzec AdapterWzorzec DekoratorWzorzec Metoda szablonowaWzorzec StanWzorzec Kompozyt3Właściwości klas
Zmienne instancji a zmienne klasoweDekoratory właściwościAtrybuty prywatnePowtórzenie — menedżer kont bankowych6Hermetyzacja
Public, Protected, Private — pamięćModyfikatory dostępuUkrywanie informacjiDekoratory właściwości — zaawansowanePodsumowanie — system ewidencji uczniów12Projekt: System zarządzania biblioteką
Przegląd projektuKlasy Book i UserSystem wypożyczeńFunkcja wyszukiwaniaInterfejs administratoraTestowanie i integracjaPoćwicz samodzielnie: Kompilator Python online