Dekoratory klas
Część sekcji Programowanie obiektowe ścieżki Python w Coddy. Lekcja 40 z 64.
Dekoratory klas pozwalają modyfikować lub rozszerzać klasy przez opakowanie ich inną funkcją. Działają jak dekoratory funkcji, ale stosuje się je do całych klas.
Oto prosta klasa bez dekorowania:
class Person:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Utwórz dekorator klasy, który dodaje nową metodę:
def add_farewell(cls):
def farewell(self):
return f"Goodbye from {self.name}"
cls.farewell = farewell # Dodaj metodę do klasy
return clsZastosuj dekorator do klasy za pomocą @:
@add_farewell
class EnhancedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Teraz klasa ma zarówno oryginalne, jak i dodane metody:
person = EnhancedPerson("Alice")
print(person.greet()) # Cześć, mam na imię Alice
print(person.farewell()) # Żegna się AliceMożesz także opakować istniejącą metodę: zachować oryginalną, aby nadal móc wywoływać ją wewnątrz opakowania. Jest to przydatne, gdy chcesz dodać dodatkowe działanie (takie jak śledzenie lub rejestrowanie) wokół metody, którą klasa już ma:
def add_tracking(cls):
original_greet = cls.greet # Zapisz oryginalną metodę
def tracked_greet(self):
print(f"greet was called") # Dodatkowe działanie przed wywołaniem
return original_greet(self) # Wywołaj oryginalną metodę
cls.greet = tracked_greet # Zastąp metodę w klasie
return cls
@add_tracking
class TrackedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Gdy teraz wywołasz greet, najpierw uruchomi się opakowanie, a następnie przekaże wywołanie oryginalnej metodzie:
person = TrackedPerson("Alice")
print(person.greet())
# Wywołano greet
# Cześć, mam na imię AliceKluczowe kroki przy opakowywaniu istniejącej metody:
1. Zapisz oryginalną metodę: original = cls.method
2. Zdefiniuj nową funkcję, która wywołuje original(self) oraz wykonuje dodatkową logikę.
3. Przypisz nową funkcję z powrotem do klasy: cls.method = new_function
4. Zwróć zmodyfikowaną klasę.
Kluczowa informacja: Dekoratory klas przyjmują klasę jako dane wejściowe, modyfikują ją lub rozszerzają i zwracają zmodyfikowaną klasę. Mogą dodawać metody, modyfikować atrybuty lub opakowywać istniejącą funkcjonalność. Aby je zastosować, umieść @decorator_name nad definicją klasy. To przejrzysty sposób na rozszerzanie funkcjonalności klasy bez dziedziczenia.
Wyzwanie
ŚredniW tym wyzwaniu zaimplementujesz dekorator klasy, który dodaje funkcjonalność śledzenia wywołań metod.
decorator.py— zaimplementuj dekorator klasyadd_countercalculator.py— zawiera klasy, które będą używać Twojego dekoratora
Plik driver.py zawiera rozbudowane scenariusze testowe, które sprawdzą Twoją implementację pod kątem różnych przypadków użycia, wzorców dziedziczenia i sytuacji brzegowych. Nie musisz modyfikować tego pliku, ale jego przejrzenie pomoże Ci zrozumieć oczekiwane działanie.
Spróbuj swoich sił
from calculator import Calculator
# Kompleksowa obsługa przypadków testowych
test_case = input()
if test_case == "basic_functionality":
calc = Calculator()
print(calc.add(5, 3)) # Powinno wypisać 8
print(calc.add(2, 7)) # Powinno wypisać 9
print(calc.subtract(10, 4)) # Powinno wypisać 6
print(calc.call_counts) # Powinno wypisać {'add': 2, 'subtract': 1}
elif test_case == "multiple_instances":
calc1 = Calculator()
calc2 = Calculator()
print(calc1.add(5, 3)) # Powinno wypisać 8
print(calc2.add(2, 7)) # Powinno wypisać 9
print(calc1.subtract(10, 4)) # Powinno wypisać 6
# Obie instancje powinny współdzielić ten sam call_counts
print(calc1.call_counts) # Powinno wypisać {'add': 2, 'subtract': 1}
print(calc2.call_counts) # Powinno wypisać {'add': 2, 'subtract': 1}
print(calc1.call_counts is calc2.call_counts) # Powinno wypisać True
elif test_case == "method_call_order":
calc = Calculator()
print(calc.add(1, 2)) # Powinno wypisać 3
print(calc.subtract(5, 2)) # Powinno wypisać 3
print(calc.add(10, 20)) # Powinno wypisać 30
print(calc.add(7, 3)) # Powinno wypisać 10
print(calc.call_counts) # Powinno wypisać {'add': 3, 'subtract': 1}
elif test_case == "large_values":
calc = Calculator()
print(calc.add(1000000, 2000000)) # Powinno wypisać 3000000
print(calc.subtract(5000000, 2000000)) # Powinno wypisać 3000000
print(calc.call_counts) # Powinno wypisać {'add': 1, 'subtract': 1}
elif test_case == "negative_values":
calc = Calculator()
print(calc.add(-5, -3)) # Powinno wypisać -8
print(calc.subtract(-10, -4)) # Powinno wyświetlić -6
print(calc.call_counts) # Powinno wypisać {'add': 1, 'subtract': 1}
elif test_case == "float_values":
calc = Calculator()
print(calc.add(5.5, 3.2)) # Powinno wyświetlić 8.7
print(calc.subtract(10.5, 4.2)) # Powinno wyświetlić 6.3
print(calc.call_counts) # Powinno wypisać {'add': 1, 'subtract': 1}
elif test_case == "zero_values":
calc = Calculator()
print(calc.add(0, 0)) # Powinno wyświetlić 0
print(calc.subtract(0, 0)) # Powinno wyświetlić 0
print(calc.call_counts) # Powinno wypisać {'add': 1, 'subtract': 1}
elif test_case == "counter_reset":
calc = Calculator()
print(calc.add(5, 3)) # Powinno wypisać 8
print(calc.call_counts) # Powinno wypisać {'add': 1, 'subtract': 0}
# Zresetuj licznik
calc.call_counts = {"add": 0, "subtract": 0}
print(calc.add(2, 7)) # Powinno wypisać 9
print(calc.call_counts) # Powinno wypisać {'add': 0, 'subtract': 0}
elif test_case == "counter_manipulation":
calc = Calculator()
print(calc.add(5, 3)) # Powinno wypisać 8
print(calc.call_counts) # Powinno wypisać {'add': 1, 'subtract': 0}
# Zmodyfikuj licznik bezpośrednio
calc.call_counts["add"] = 100
print(calc.add(2, 7)) # Powinno wypisać 9
print(calc.call_counts) # Powinno wypisać {'add': 101, 'subtract': 0}
elif test_case == "performance_test":
calc = Calculator()
# Wykonaj 1000 operacji add
for i in range(1000):
calc.add(i, i+1)
# Wykonaj 500 operacji subtract
for i in range(500):
calc.subtract(i+10, i)
print(calc.call_counts) # Powinno wypisać {'add': 1000, 'subtract': 500}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