Сегодня погружаемся в продвинутые аспекты ООП в Python: магические методы и декораторы. Эти инструменты позволяют создавать более выразительный и функциональный код. Магические методы позволяют изменить поведение объектов класса, а декораторы помогают добавлять функциональность к функциям и классам, не изменяя их исходный код.
В конце статьи мы напишем программу с использованием декоратора, который будет логировать вызовы функций.
Магические Методы
Магические методы (или «дуnder methods») – это встроенные методы с двумя подчеркиваниями в начале и конце имени, такие как __init__, __str__, __repr__, и другие. Они предоставляют объектам классов дополнительные возможности, такие как изменение их представления или поведение в операциях.
Основные Магические Методы
__init__ — конструктор, инициализирующий объект при его создании.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
__str__ — возвращает строковое представление объекта для пользователей. Этот метод используется, когда объект передается в print().
def __str__(self):
return f"{self.name}, {self.age} years old"
__repr__ — возвращает строковое представление объекта, предназначенное для разработчиков. Используется, например, в интерактивной оболочке Python. Обычно __repr__ дает больше информации об объекте, чем __str__.
def __repr__(self):
return f"Person(name='{self.name}', age={self.age})"
__len__ — позволяет определить длину объекта, если это применимо (например, для списков, строк и т.д.).
def __len__(self):
return len(self.name) # Например, возвращаем длину имени
__getitem__, __setitem__, __delitem__ — позволяют обращаться к объекту, как к словарю или списку. Полезно, если вы хотите создать пользовательский контейнер данных.
__call__ — позволяет сделать объект вызываемым, как функцию.
def __call__(self, greeting):
return f"{greeting}, {self.name}!"
Пример использования магических методов
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name}, {self.age} years old"
def __repr__(self):
return f"Person(name='{self.name}', age={self.age})"
def __call__(self, greeting):
return f"{greeting}, {self.name}!"
# Использование
p = Person("Alice", 30)
print(p) # Alice, 30 years old
print(repr(p)) # Person(name='Alice', age=30)
print(p("Hello")) # Hello, Alice!
Декораторы
Декораторы — это функции, которые модифицируют другие функции или классы, добавляя к ним функциональность. Декораторы особенно полезны, если необходимо выполнять код до или после основной функции, не изменяя её структуру.
Основы Декораторов
Декораторы создаются как функции, которые принимают другую функцию и возвращают новую функцию.
Пример простого декоратора:
def my_decorator(func):
def wrapper():
print("Something before the function.")
func()
print("Something after the function.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
При вызове say_hello() мы увидим:
Something before the function.
Hello!
Something after the function.
Декораторы с аргументами
Если нужно передавать аргументы декорируемой функции, оборачивающая функция (wrapper) также должна принимать аргументы.
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Начало выполнения функции.")
result = func(*args, **kwargs)
print("Конец выполнения функции.")
return result
return wrapper
@my_decorator
def add(x, y):
return x + y
print(add(2, 3))
Декораторы для классов
Декораторы можно применять и к классам, модифицируя их поведение при создании объектов. Например, можно автоматически добавлять методы или атрибуты к каждому экземпляру.
Пример программы: Декоратор для логирования вызовов функций
Создадим декоратор log_calls, который будет выводить информацию о каждом вызове функции, включая её имя, переданные аргументы и результат.
from datetime import datetime
def log_calls(func):
def wrapper(*args, **kwargs):
start_time = datetime.now()
print(f"Вызов {func.__name__}() с аргументами {args} и {kwargs}")
result = func(*args, **kwargs)
end_time = datetime.now()
print(f"{func.__name__}() вернула {result}")
print(f"Время выполнения: {end_time - start_time}")
return result
return wrapper
@log_calls
def multiply(a, b):
return a * b
@log_calls
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
# Примеры вызовов
multiply(3, 4)
greet("Alice", greeting="Hi")
Объяснение работы программы
Декоратор log_calls — оборачивает переданную функцию в wrapper, которая логирует информацию о вызове.
При каждом вызове декорированной функции выводятся её имя, аргументы, результат выполнения и время работы.
Вывод программы
При вызове multiply(3, 4) и greet(«Alice», greeting=»Hi») мы увидим примерно следующее:
Вызов multiply() с аргументами (3, 4) и {}
multiply() вернула 12
Время выполнения: 0:00:00.000002
Вызов greet() с аргументами ('Alice',) и {'greeting': 'Hi'}
greet() вернула Hi, Alice!
Время выполнения: 0:00:00.000001
Заключение
Магические методы и декораторы добавляют гибкость в Python, позволяя создавать расширяемые и легко читаемые классы и функции. Магические методы, такие как __str__, __repr__, __call__, делают объекты более функциональными, а декораторы помогают добавить новые возможности без изменения исходного кода.