📝 Практические задания

⚡ Задания в двух словах

  1. Декоратор limit_output обрезает строку до 20 символов.
  2. Декоратор limit_output(max_len) обрезает строку до заданной длины.
  3. Декоратор cache запоминает результаты для каждого набора аргументов.
  4. Декоратор cache(max_size) ограничивает размер кэша и вытесняет старые записи.
  5. Декоратор log_to_file записывает вызовы в call_log.log.
  6. Декоратор accepts_types проверяет позиционные аргументы по типам.

Задание 1. Ограничение длины строки

Условие: создайте декоратор limit_output, который обрезает строку, возвращаемую функцией, до max_len символов. В данном задании максимальная длина фиксирована внутри декоратора.

Если строка длиннее, то она обрезается до выбранного лимита и завершается троеточием ..., входящим в этот лимит.

Пример применения:

@limit_output
def get_text():
    return "Это очень длинный текст, который нужно обрезать."

Пример вывода:

Это очень длинный...

Задание 2. Ограничение с указанием длины строки

Условие: доработайте декоратор limit_output, чтобы он принимал параметр max_len — максимальную длину строки.

Пример применения:

@limit_output(26)
def get_text():
    return "Это очень длинный текст, который нужно обрезать."

Пример вывода:

Это очень длинный текст...

Задание 3. Кеширование результатов

Условие: создайте декоратор cache, который сохраняет результат вызова функции для каждого набора аргументов. Если функция вызывается повторно с теми же аргументами — возвращается сохранённый результат.

Пример применения:

@cache
def multiply(a, b):
    print(f"Вычисляем {a} * {b}: ")
    return a * b

Пример вывода:

Вычисляем 2 * 3:
6
Результат из кеша:
6
Вычисляем 4 * 5:
20
Результат из кеша:
6

Задание 4. Кеш с ограничением по размеру

Условие: доработайте декоратор cache, чтобы он:

  • Принимал параметр max_size, ограничивающий количество сохранённых записей.
  • При превышении max_size удалял самую старую запись из кэша.

Пример применения:

@cache(max_size=2)
def multiply(a, b):
    print(f"Вычисляем {a} * {b}: ")
    return a * b

Пример вывода:

Вычисляем 2 * 3:
6
Результат из кеша:
6
Вычисляем 4 * 5:
20
Вычисляем 6 * 7:
42
Вычисляем 2 * 3:
6

Задание 5. Журнал вызовов функции

Условие: создайте декоратор log_to_file, который будет записывать в файл все вызовы функции с её аргументами и результатом. Лог сохраняется в файл call_log.log, каждый вызов — на новой строке.

Пример применения:

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

@log_to_file
def greet_with_name(name, punctuation="!"):
    return f"Hello, {name}{punctuation}"

@log_to_file
def greet():
    return "Hello"

Пример вывода (файл call_log.log):

function: add | args: 5, 3; kwargs: None | return: 8
function: greet_with_name | args: Alice; kwargs: punctuation='.' | return: Hello, Alice.
function: greet | args: None; kwargs: None | return: Hello

Задание 6. Проверка типов аргументов по порядку

Условие: создайте декоратор accepts_types, который проверяет, что позиционные аргументы функции соответствуют указанным типам. Если тип хотя бы одного аргумента не совпадает — должна быть выброшена ошибка TypeError с указанием ожидаемого и полученного типа.

Пример применения:

@accepts_types(int, int)
def add(a, b):
    return a + b

@accepts_types(str)
def greet(name):
    return f"Hello, {name}!"

@accepts_types(str, str)
def greet_by_name(name, punctuation="!"):
    return f"Hello, {name}{punctuation}"

Пример вызова:

try:
    print(add(5, 3))
    print(greet_by_name("Anna"))
    print(add("one", 3))             # ошибка
    print(greet_by_name("Anna", 0))  # ошибка
except Exception as e:
    print(e)

Пример вывода:

8
Hello, Anna!
Incorrect type for argument 'one': expected - <class 'int'>, received - <class 'str'>
Incorrect type for argument '0': expected - <class 'str'>, received - <class 'int'>

Проверка понимания

Выберите один вариант ответа. После клика правильный ответ подсветится зелёным.

Вопрос 1

Что важнее всего в практическом задании?

Вопрос 2

Как безопаснее двигаться в практикуме?

Вопрос 3

Что делать, если решение не совпадает с ожидаемым результатом?

Вопрос 4

Какой стиль имён переменных обычно рекомендует PEP 8?

Вопрос 5

Зачем использовать f-strings в современном Python?

Вопрос 6

Когда полезен ранний возврат из функции?