Пример 1. Обрезка строки
Декоратор без параметров фиксирует максимальную длину внутри себя. Если результат функции длиннее лимита, он обрезается, а в конец добавляется троеточие.
def limit_output(func):
max_len = 20
def wrapper() -> str:
result = func()
if len(result) <= max_len:
return result
return result[:max_len - 3] + "..."
return wrapper
@limit_output
def get_text() -> str:
return "Это очень длинный текст, который нужно обрезать."
print(get_text()) # Это очень длинный...
Что происходит:
- Декоратор запоминает
max_len = 20в замыкании. - При вызове
get_text()обёртка получает строку длиной 49 символов. - Так как 49 > 20, возвращаются первые 17 символов плюс
....
Пример 2. Параметризованная обрезка
Параметризованный декоратор позволяет задавать лимит при применении.
def limit_output(max_len: int):
def decorator(func):
def wrapper() -> str:
result = func()
if len(result) <= max_len:
return result
return result[:max_len - 3] + "..."
return wrapper
return decorator
@limit_output(26)
def get_text() -> str:
return "Это очень длинный текст, который нужно обрезать."
print(get_text()) # Это очень длинный текст...
Пример 3. Простое кэширование
Кэш хранит результаты вычислений для каждого набора позиционных аргументов.
def cache(func):
memo: dict[tuple, int] = {}
def wrapper(*args):
if args not in memo:
memo[args] = func(*args)
else:
print("Результат из кеша: ")
return memo[args]
return wrapper
@cache
def multiply(a: int, b: int) -> int:
print(f"Вычисляем {a} * {b}: ")
return a * b
print(multiply(2, 3))
print(multiply(2, 3))
print(multiply(4, 5))
print(multiply(2, 3))
Пример 4. Кэш с ограничением размера
Когда кэш заполнен, самая старая запись удаляется. OrderedDict позволяет обновлять порядок при повторном обращении.
from collections import OrderedDict
def cache(max_size: int = 5):
def decorator(func):
memo: OrderedDict[tuple, int] = OrderedDict()
def wrapper(*args):
if args in memo:
print("Результат из кеша: ")
memo.move_to_end(args)
else:
result = func(*args)
if len(memo) >= max_size:
memo.popitem(last=False)
memo[args] = result
return memo[args]
return wrapper
return decorator
@cache(max_size=2)
def multiply(a: int, b: int) -> int:
print(f"Вычисляем {a} * {b}: ")
return a * b
print(multiply(2, 3))
print(multiply(2, 3))
print(multiply(4, 5))
print(multiply(6, 7))
print(multiply(2, 3)) # будет пересчитан
Пример 5. Логирование в файл
Декоратор записывает имя функции, аргументы и результат в файл call_log.log.
import logging
logging.basicConfig(
filename="call_log.log",
level=logging.INFO,
format="%(message)s",
encoding="utf-8",
)
def log_to_file(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
all_args = ", ".join(str(arg) for arg in args)
all_kwargs = ", ".join(f"{k}={v!r}" for k, v in kwargs.items())
logging.info(
f"function: {func.__name__} | "
f"args: {all_args if all_args else None}; "
f"kwargs: {all_kwargs if all_kwargs else None} | "
f"return: {result}"
)
return result
return wrapper
@log_to_file
def add(a: int, b: int) -> int:
return a + b
@log_to_file
def greet_with_name(name: str, punctuation: str = "!") -> str:
return f"Hello, {name}{punctuation}"
@log_to_file
def greet() -> str:
return "Hello"
add(5, 3)
greet_with_name("Alice", punctuation=".")
greet()
Пример 6. Проверка типов
Декоратор проверяет позиционные аргументы по порядку. При несовпадении типов выбрасывает TypeError.
def accepts_types(*expected_types):
def decorator(func):
def wrapper(*args):
for arg, expected in zip(args, expected_types):
if not isinstance(arg, expected):
raise TypeError(
f"Incorrect type for argument '{arg}': "
f"expected - {expected}, received - {type(arg)}"
)
return func(*args)
return wrapper
return decorator
@accepts_types(int, int)
def add(a: int, b: int) -> int:
return a + b
@accepts_types(str)
def greet(name: str) -> str:
return f"Hello, {name}!"
@accepts_types(str, str)
def greet_by_name(name: str, punctuation: str = "!") -> str:
return f"Hello, {name}{punctuation}"
try:
print(add(5, 3))
print(greet_by_name("Anna"))
print(greet_by_name("Anna", 0))
except Exception as e:
print(e)
Как запустить в VS Code
- Создайте файл
practice_16.pyв рабочей папке. - Скопируйте код одного из примеров выше.
- Откройте терминал в VS Code (Ctrl + `).
- Убедитесь, что активировано виртуальное окружение:
venv\Scripts\activate(Windows) илиsource venv/bin/activate(Mac/Linux). - Запустите:
python practice_16.py