Menu
Coddy logo textTech

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 cls

Zastosuj 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ę Alice

Moż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ę Alice

Kluczowe 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.

challenge icon

Wyzwanie

Średni

W tym wyzwaniu zaimplementujesz dekorator klasy, który dodaje funkcjonalność śledzenia wywołań metod.

  • decorator.py — zaimplementuj dekorator klasy add_counter
  • calculator.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}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Python online