💻 Практические примеры

⚡ Минимальный рабочий пример

import functools


def greeting_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("Начало")
        result = func(*args, **kwargs)
        print("Конец")
        return result
    return wrapper


@greeting_decorator
def say_hi(name):
    return f"Привет, {name}!"


print(say_hi("Python"))


class User:
    def __init__(self, name):
        self.name = name

    def greet(self):
        return f"Hello, {self.name}"


user = User("Alice")
print(user.greet())

Пример 1. Логирование вызовов

import logging

logging.basicConfig(
    filename="functions.log",
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    encoding="utf-8"
)


def log_decorator(func):
    def wrapper(*args, **kwargs):
        logging.info(f"Функция {func.__name__} вызвана с аргументами: {args}, {kwargs}")
        result = func(*args, **kwargs)
        logging.info(f"Функция {func.__name__} вернула: {result}")
        return result
    return wrapper


@log_decorator
def add(a, b):
    return a + b


@log_decorator
def say_hello():
    print("Привет!")


print(add(3, 5))
say_hello()

Пояснение: декоратор записывает в файл functions.log имя функции, аргументы и результат. Обёртка принимает любые аргументы и возвращает результат, поэтому декорируемые функции работают как обычно.

Пример 2. Потеря результата в декораторе

def upper_decorator(func):
    def wrapper(*args, **kwargs):
        print("Выполняем функцию, но ничего не возвращаем")
        func(*args, **kwargs).upper()
    return wrapper


@upper_decorator
def get_text():
    return "hello"


result = get_text()
print("Результат:", result)

Вывод: Результат: None. Результат вызова func не возвращается наружу, поэтому декорированная функция возвращает None.

Пример 3. Возврат результата из декоратора

def upper_decorator(func):
    def wrapper(*args, **kwargs):
        print("Выполняем функцию и возвращаем результат")
        return func(*args, **kwargs).upper()
    return wrapper


@upper_decorator
def get_text():
    return "hello"


result = get_text()
print("Результат:", result)

Вывод: Результат: HELLO. Теперь обёртка возвращает преобразованный результат.

Пример 4. Проблема без functools.wraps

def simple_decorator(func):
    def wrapper(*args, **kwargs):
        """Вложенная функция wrapper"""
        print("\nДекорированная функция: ")
        print(f"Оригинальное имя функции: {func.__name__}")
        print(f"Оригинальная документация: {func.__doc__}")
        return func(*args, **kwargs)
    return wrapper


@simple_decorator
def example_function():
    """Это оригинальная функция."""
    print("Привет!")


print("Имя декорированной функции:", example_function.__name__)
print("Документация декорированной функции:", example_function.__doc__)
example_function()

Без functools.wraps атрибуты __name__ и __doc__ принадлежат wrapper, а не оригинальной функции.

Пример 5. Решение с functools.wraps

import functools


def simple_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("\nДекорированная функция")
        return func(*args, **kwargs)
    return wrapper


@simple_decorator
def example_function():
    """Это оригинальная функция."""
    print("Привет!")


print("Имя декорированной функции:", example_function.__name__)
print("Документация декорированной функции:", example_function.__doc__)
example_function()

Теперь метаданные оригинала сохраняются.

Пример 6. Декоратор с настраиваемым сообщением

def message_decorator(message):
    def decorator(func):
        def wrapper():
            print(message)
            return func()
        return wrapper
    return decorator


@message_decorator("Начинаем выполнение")
def analise_data():
    print("Данные проанализированы")


@message_decorator("Загрузка данных...")
def load_data():
    print("Данные загружены")


analise_data()
print()
load_data()

message_decorator — фабрика: она не оборачивает функцию сразу, а возвращает декоратор, который уже делает обёртку.

Пример 7. Автоматический retry

import time


def retry(attempts):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"Попытка {i+1} не удалась: {e}")
                    time.sleep(5)
            print("Все попытки исчерпаны.")
        return wrapper
    return decorator


@retry(3)
def get_data(filename):
    """Читает данные из файла"""
    with open(filename, "r", encoding="utf-8") as file:
        return file.read()


# Для запуска создайте рядом файл data.txt
data = get_data("data.txt")
if data:
    print("Содержимое файла:")
    print(data)

Пример 8. Цепочка декораторов

def border_decorator(func):
    def wrapper():
        print("*" * 100)
        func()
        print("*" * 100)
    return wrapper


def repeat_decorator(func):
    def wrapper():
        for _ in range(3):
            func()
    return wrapper


@border_decorator
@repeat_decorator
def print_line():
    print("-" * 100)


print_line()

Сначала применяется repeat_decorator, затем border_decorator. В результате строка повторяется трижды внутри рамки.

Пример 9. Создание объектов класса Book

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author


book1 = Book("1984", "George Orwell")
book2 = Book("Brave New World", "Aldous Huxley")
print(book1.title, book1.author)
print(book2.title, book2.author)

Пример 10. Доступ и изменение полей

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author


book = Book("1984", "George Orwell")
print(book.title)  # старое имя
print(book.author)
book.title = "Animal Farm"  # изменение значения поля title
print(book.title)  # измененное имя

Пример 11. Методы класса

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

    def get_description(self):
        return f"{self.title} — {self.author}"

    def show_info(self):
        print(self.get_description())

    def make_author_upper(self):
        self.author = self.author.upper()


book = Book("1984", "George Orwell")
book.show_info()
book.make_author_upper()
book.show_info()

Пример 12. Класс Circle

import math


class Circle:
    def __init__(self, radius):
        self.radius = radius

    def get_area(self):
        return math.pi * self.radius ** 2


circle = Circle(5)
print(f"Площадь: {circle.get_area():.1f}")
circle.radius = 8
print(f"Новая площадь: {circle.get_area():.1f}")

Примерный вывод:

Площадь: 78.5
Новая площадь: 201.1

Как запустить в VS Code

  1. Создайте папку python-fundamentals-practice/lesson-67/.
  2. Создайте файл summary_session_17.py и скопируйте в него примеры.
  3. Откройте терминал в VS Code (Ctrl + `).
  4. Активируйте виртуальное окружение: venv\Scripts\activate (Windows) или source venv/bin/activate (Mac/Linux).
  5. Запустите: python summary_session_17.py