✅ Решения заданий

⚠️ Рекомендация: попробуйте решить задания самостоятельно, прежде чем смотреть ответы. Решения — это последняя инстанция, когда собственные попытки исчерпаны.

⚡ Кратко

  • Задание 1: фабрика умножает цену на 1 + rate.
  • Задание 2: скидка ищется по категории, иначе возвращается исходная цена.
  • Задание 3: *args передаётся в print с зафиксированными sep и end.
  • Задание 4: счётчик в замыкании увеличивается через nonlocal.

Решение 1. Фабрика функций расчёта НДС

def vat_calculator(rate: float):
    def calculate(price: float) -> float:
        return round(price * (1 + rate), 2)

    return calculate


vat_20 = vat_calculator(0.2)
vat_10 = vat_calculator(0.1)

print(vat_20(100))  # 120.0
print(vat_10(200))  # 220.0

Логика: внешняя функция запоминает rate в замыкании. Каждый вызов vat_calculator создаёт независимую функцию со своей ставкой.

Решение 2. Калькулятор скидок по категориям

def discount_maker(category_discounts: dict[str, float]):
    def apply_discount(category: str, price: float) -> float:
        discount = category_discounts.get(category, 0)
        return round(price * (1 - discount), 2)

    return apply_discount


discounts = {"food": 0.1, "clothes": 0.2}
friday_discount = discount_maker(discounts)

print(friday_discount("food", 100))          # 90.0
print(friday_discount("clothes", 250))       # 200.0
print(friday_discount("electronics", 500))   # 500

Логика: category_discounts захватывается из Enclosing-области. Метод dict.get возвращает 0, если категория не найдена, поэтому цена не меняется.

Решение 3. Настроенная функция вывода

def custom_printer(sep: str, end: str):
    def print_func(*args) -> None:
        print(*args, sep=sep, end=end)

    return print_func


printer = custom_printer(sep=' | ', end=' -->\n')
printer('Hello', 'World')
printer('Python', 'Java', 'C++')

Логика: *args собирает все позиционные аргументы, а sep и end берутся из замыкания. Значения по умолчанию нельзя переопределить при вызове printer, что и требуется по условию.

Решение 4. Нумерация вызовов функции

def call_counter(func):
    count = 0

    def wrapper() -> None:
        nonlocal count
        count += 1
        print(f"Вызов функции '{func.__name__}' №{count}:")
        func()

    return wrapper


@call_counter
def greet():
    print("Привет!")


greet()
greet()
greet()

Логика: декоратор возвращает вложенную функцию wrapper. Счётчик count хранится в замыкании и увеличивается через nonlocal при каждом вызове.

Проверить по документации: декораторы с сохранением метаданных функции часто используют functools.wraps. Подробнее — в документации functools.