Декораторы классов
Часть раздела Объектно-ориентированное программирование путешествия по Python на Coddy. Урок 40 из 64.
Декораторы классов позволяют изменять или расширять классы, оборачивая их другой функцией. Они работают как декораторы функций, но применяются ко всему классу.
Вот простой класс без декоратора:
class Person:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Создай декоратор класса, который добавляет новый метод:
def add_farewell(cls):
def farewell(self):
return f"Goodbye from {self.name}"
cls.farewell = farewell # Добавить метод в класс
return clsПримените декоратор к классу с помощью @:
@add_farewell
class EnhancedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Теперь у класса есть и исходные, и добавленные методы:
person = EnhancedPerson("Alice")
print(person.greet()) # Привет, меня зовут Алиса
print(person.farewell()) # До свидания от АлисыВы также можете обернуть существующий метод: сохранить исходный метод, чтобы по-прежнему вызывать его внутри обёртки. Это полезно, когда вы хотите добавить поведение (например, отслеживание или ведение журнала) вокруг метода, который уже есть у класса:
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. Верните изменённый класс.
Ключевой момент: декораторы классов принимают класс в качестве входных данных, изменяют или расширяют его и возвращают изменённый класс. Они могут добавлять методы, изменять атрибуты или оборачивать существующие возможности. Чтобы применить декоратор, укажи @decorator_name перед определением класса. Это позволяет удобно расширять функциональность класса без наследования.
Задание
СреднеВ этом задании вам предстоит реализовать декоратор класса, который добавляет отслеживание вызовов методов.
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) # Должно вывести {'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) # Должно вывести {'add': 2, 'subtract': 1}
print(calc2.call_counts) # Должно вывести {'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) # Должно вывести {'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) # Должно вывести {'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) # Должно вывести {'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) # Должно вывести {'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) # Должно вывести {'add': 1, 'subtract': 1}
elif test_case == "counter_reset":
calc = Calculator()
print(calc.add(5, 3)) # Должно вывести 8
print(calc.call_counts) # Должно вывести {'add': 1, 'subtract': 0}
# Сбросить счётчик
calc.call_counts = {"add": 0, "subtract": 0}
print(calc.add(2, 7)) # Должно вывести 9
print(calc.call_counts) # Должно вывести {'add': 0, 'subtract': 0}
elif test_case == "counter_manipulation":
calc = Calculator()
print(calc.add(5, 3)) # Должно вывести 8
print(calc.call_counts) # Должно вывести {'add': 1, 'subtract': 0}
# Изменить счётчик напрямую
calc.call_counts["add"] = 100
print(calc.add(2, 7)) # Должно вывести 9
print(calc.call_counts) # Должно вывести {'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) # Должно вывести {'add': 1000, 'subtract': 500}В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Введение в ООПВнешние файлыКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — простой калькулятор4Наследование
Основы наследованияФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовПовторение: иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн «Одиночка»Паттерн «Фабрика»Паттерн «Наблюдатель»Паттерн «Стратегия»2Декораторы
Введение в декораторыДекоратор свойстваДекоратор статического методаДекоратор метода класса5Полиморфизм
Переопределение методов: повторениеУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовПовторение — калькулятор фигур8Продвинутые концепции ООП
Композиция и наследованиеМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыПовторение — менеджер банковского счёта6Инкапсуляция
Публичные, защищённые и приватные членыМодификаторы доступаСокрытие информацииПродвинутые декораторы свойствПовторение — система учёта студентов12Проект: Система управления библиотекой
Обзор проектаКлассы книг и пользователейСистема выдачи книгФункция поискаИнтерфейс администратораТестирование и интеграцияПотренируйтесь самостоятельно: Онлайн-компилятор Python