Декораторы классов
Часть раздела Объектно-ориентированное программирование путешествия по Python на Coddy. Урок 40 из 64.
Декораторы классов позволяют изменять или расширять классы, оборачивая их в другую функцию. Они работают подобно декораторам функций, но применяются ко всему классу.
Вот простой class без декорации:
class Person:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Создайте class-декоратор, который добавляет новый method:
def add_farewell(cls):
def farewell(self):
return f"Goodbye from {self.name}"
cls.farewell = farewell # Добавить метод в класс
return clsПримени decorator к классу, используя @:
@add_farewell
class EnhancedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Теперь у класса есть both original и добавленные methods:
person = EnhancedPerson("Alice")
print(person.greet()) # Привет, меня зовут Alice
print(person.farewell()) # До свидания от AliceВы также можете обернуть существующий method: сохранить original, чтобы по-прежнему вызывать его внутри обёртки. Это полезно, когда вы хотите добавить behaviour (например, отслеживание или ведение журнала) вокруг method, который уже есть в class:
def add_tracking(cls):
original_greet = cls.greet # Сохранить оригинальный метод
def tracked_greet(self):
print(f"greet was called") # Дополнительное поведение перед
return original_greet(self) # Вызвать оригинальный метод
cls.greet = tracked_greet # Заменить метод в классе
return cls
@add_tracking
class TrackedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Теперь, когда ты вызываешь greet, сначала выполняется обёртка, а затем она делегирует вызов оригинальному методу:
person = TrackedPerson("Alice")
print(person.greet())
# greet был вызван
# Привет, меня зовут AliceКлючевые шаги для обёртывания существующего метода:
1. Сохранить исходный метод: original = cls.method
2. Определить новую функцию, которая вызывает original(self), а также выполняет дополнительную логику.
3. Присвоить новую функцию обратно классу: cls.method = new_function
4. Вернуть изменённый класс.
Ключевой момент: class-декораторы принимают class в качестве input, выполняют Modify или Enhance его и возвращают modified class. Они могут add methods, Modify атрибуты или оборачивать существующую functionality. Используйте @decorator_name above class definition, чтобы применить их. Это предоставляет чистый способ расширить functionality class без наследования.
Задание
СреднеВ этом задании вам предстоит реализовать декоратор класса, который добавляет функциональность отслеживания вызовов методов.
decorator.py— реализуйте декоратор классаadd_countercalculator.py— содержит классы, которые будут использовать ваш декоратор
Файл driver.py содержит обширные сценарии тестирования, которые проверят вашу реализацию в различных случаях использования, шаблонах наследования и граничных случаях. Вам не нужно изменять этот файл, но его изучение поможет понять ожидаемое поведение.
Попробуйте сами
from calculator import Calculator
# Комплексный обработчик тестовых случаев
test_case = input()
if test_case == "basic_functionality":
calc = Calculator()
print(calc.add(5, 3)) # Должно вывести 8
print(calc.add(2, 7)) # Должно вывести 9
print(calc.subtract(10, 4)) # Должно вывести 6
print(calc.call_counts) # Should print {'add': 2, 'subtract': 1}
elif test_case == "multiple_instances":
calc1 = Calculator()
calc2 = Calculator()
print(calc1.add(5, 3)) # Должно вывести 8
print(calc2.add(2, 7)) # Должно вывести 9
print(calc1.subtract(10, 4)) # Должно вывести 6
# Оба экземпляра должны использовать один и тот же call_counts
print(calc1.call_counts) # Should print {'add': 2, 'subtract': 1}
print(calc2.call_counts) # Should print {'add': 2, 'subtract': 1}
print(calc1.call_counts is calc2.call_counts) # Должно вывести True
elif test_case == "method_call_order":
calc = Calculator()
print(calc.add(1, 2)) # Должно вывести 3
print(calc.subtract(5, 2)) # Должно вывести 3
print(calc.add(10, 20)) # Должно вывести 30
print(calc.add(7, 3)) # Должно вывести 10
print(calc.call_counts) # Should print {'add': 3, 'subtract': 1}
elif test_case == "large_values":
calc = Calculator()
print(calc.add(1000000, 2000000)) # Должно вывести 3000000
print(calc.subtract(5000000, 2000000)) # Должно вывести 3000000
print(calc.call_counts) # Should print {'add': 1, 'subtract': 1}
elif test_case == "negative_values":
calc = Calculator()
print(calc.add(-5, -3)) # Должно вывести -8
print(calc.subtract(-10, -4)) # Должно вывести -6
print(calc.call_counts) # Should print {'add': 1, 'subtract': 1}
elif test_case == "float_values":
calc = Calculator()
print(calc.add(5.5, 3.2)) # Должно вывести 8.7
print(calc.subtract(10.5, 4.2)) # Должно вывести 6.3
print(calc.call_counts) # Should print {'add': 1, 'subtract': 1}
elif test_case == "zero_values":
calc = Calculator()
print(calc.add(0, 0)) # Должно вывести 0
print(calc.subtract(0, 0)) # Должно вывести 0
print(calc.call_counts) # Should print {'add': 1, 'subtract': 1}
elif test_case == "counter_reset":
calc = Calculator()
print(calc.add(5, 3)) # Должно вывести 8
print(calc.call_counts) # Should print {'add': 1, 'subtract': 0}
# Сбросить счётчик
calc.call_counts = {"add": 0, "subtract": 0}
print(calc.add(2, 7)) # Должно вывести 9
print(calc.call_counts) # Should print {'add': 0, 'subtract': 0}
elif test_case == "counter_manipulation":
calc = Calculator()
print(calc.add(5, 3)) # Должно вывести 8
print(calc.call_counts) # Should print {'add': 1, 'subtract': 0}
# Изменить счётчик напрямую
calc.call_counts["add"] = 100
print(calc.add(2, 7)) # Должно вывести 9
print(calc.call_counts) # Should print {'add': 101, 'subtract': 0}
elif test_case == "performance_test":
calc = Calculator()
# Выполнить 1000 операций add
for i in range(1000):
calc.add(i, i+1)
# Выполнить 500 операций subtract
for i in range(500):
calc.subtract(i+10, i)
print(calc.call_counts) # Should print {'add': 1000, 'subtract': 500}В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — Простой калькулятор4Наследование
Базовое наследованиеФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовИтоги — Иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy5Полиморфизм
Снова о переопределении методовУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовИтоги — Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыИтоги — Менеджер банковского счета6Инкапсуляция
Public, Protected, Private члены классаМодификаторы доступаСокрытие данныхДекораторы property: продвинутый уровеньИтоги: Система учета студентов12Проект: Управление библиотекой
Обзор проектаКлассы Book и UserСистема выдачи книгФункционал поискаИнтерфейс администратораТестирование и интеграцияПотренируйтесь самостоятельно: Онлайн-компилятор Python