✅ Решения

⚡ Кратко

Решения построены на трёх уровнях вложенности для декораторов с аргументами и используют functools.wraps. Всегда возвращайте результат оригинальной функции.

Решение 1. Рамка-обводка

import functools


def framed(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        border = "=" * 40
        print(border)
        result = func(*args, **kwargs)
        print(border)
        return result
    return wrapper


@framed
def show_title() -> None:
    print("== Menu ==")


show_title()

Решение 2. Настраиваемая рамка

import functools


def framed(width: int, symbol: str = "="):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            border = symbol * width
            print(border)
            result = func(*args, **kwargs)
            print(border)
            return result
        return wrapper
    return decorator


@framed(30, "-")
def show_title() -> None:
    print("== Menu ==")


show_title()

Решение 3. Декоратор upper

import functools


def upper_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        if not isinstance(result, str):
            raise TypeError("upper_decorator works only with strings")
        return result.upper()
    return wrapper


@upper_decorator
def get_text() -> str:
    return "hello"


print(get_text())  # HELLO

Решение 4. Retry

import time
import functools


def retry(attempts: int, delay: float = 1.0):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as exc:
                    print(f"Attempt {attempt} failed: {exc}")
                    if attempt == attempts:
                        print("All attempts exhausted.")
                        return None
                    time.sleep(delay)
            return None
        return wrapper
    return decorator


@retry(3, delay=0.5)
def unstable_function() -> str:
    import random
    if random.random() < 0.7:
        raise ConnectionError("Network error")
    return "Success"


print(unstable_function())

Общие принципы

  • Универсальная обёртка принимает *args, **kwargs.
  • Результат оригинала возвращается наружу.
  • @functools.wraps сохраняет метаданные.
  • Декоратор с аргументами — фабрика с тремя уровнями вложенности.