Перейти к содержимому
На этой странице

Итераторы, генераторы, и декораторы

Все темы Python Developer

Что такое декоратор?

Декоратор в Python - это функция, которая позволяет изменять поведение другой функции без изменения ее кода. Он принимает функцию в качестве аргумента и возвращает другую функцию. Декораторы часто используются для добавления функциональности к существующим функциям.

Пример:
def my_decorator(func):
    def wrapper():
        print("До вызова функции")
        func()
        print("После вызова функции")
    return wrapper

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

say_hello()

Этот код создает декоратор my_decorator, который добавляет вывод сообщений до и после вызова функции say_hello(). Использование декоратора @my_decorator перед определением функции say_hello() применяет этот декоратор к функции say_hello().


Чем отличается декоратор от итератора?

Декоратор и итератор - это два различных концепта в Python.

Декоратор:

  • Декоратор в Python - это функция, которая позволяет изменять поведение другой функции без изменения ее кода.

  • Он принимает функцию в качестве аргумента и возвращает другую функцию.

  • Декораторы часто используются для добавления функциональности к существующим функциям.

Итератор:

  • Итератор в Python - это объект, который представляет последовательность элементов.

  • Он обеспечивает методы для поочередного доступа к элементам последовательности.

  • Итераторы используются для эффективного перебора элементов в цикле или другой операции, такой как генераторы списков.

Таким образом, основное отличие между декоратором и итератором заключается в их назначении и способе использования. Декораторы изменяют поведение функций, а итераторы предоставляют возможность эффективно перебирать элементы последовательности.


Чем отличаются итераторы от генераторов?

Итераторы могут быть созданы с использованием классов или функции iter(), а генераторы создаются с использованием ключевого слова yield. Генераторы являются удобным итераторным подходом для создания последовательностей данных.


Каков синтаксис итератора в Python и для каких целей он используются?

Итератор в Python представляет собой объект, который предоставляет методы для поочередного доступа к элементам последовательности. Синтаксис итератора в Python включает использование функций iter() и next(). Функция iter() принимает объект и возвращает его итератор, а функция next() используется для получения следующего элемента из итератора.

Метод __iter__() в классе используется для создания итератора, который возвращает себя. Метод __next__() возвращает следующий элемент последовательности и вызывается функцией next() для доступа к последующим элементам.

Синтаксис:
# Создание итератора
my_iter = iter(iterable)

# Получение следующего элемента
next_element = next(my_iter)

Итераторы используются для эффективного перебора элементов в цикле или других операциях, таких как генераторы списков, фильтрация, суммирование и другие манипуляции с данными. Они позволяют работать с большими объемами данных, не загружая их все в память одновременно.


Каков синтаксис генератора в Python и для каких целей он используются?

Генератор в Python - это специальный тип итератора, который создается с использованием ключевого слова yield. Он используется для генерации последовательности значений без необходимости загрузки их всех в память одновременно. Генераторы удобны для создания итераторов в функциональном стиле с минимальным объемом кода.

Синтаксис:
def my_generator():
    yield value

Генератор определяется с использованием ключевого слова def, а затем вместо оператора return используется оператор yield, который возвращает значение и приостанавливает выполнение функции до следующего вызова.


Приведите несколько примеров декораторов в Python.

Вот несколько примеров декораторов в Python: Декоратор для замера времени выполнения функции:
import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Function {func.__name__} executed in {end_time - start_time} seconds")
        return result
    return wrapper

@timer
def example_function():
    time.sleep(2)
    print("Function executed")

example_function()
Декоратор для проверки аргументов функции:
def validate_input(func):
    def wrapper(*args, **kwargs):
        for arg in args:
            if not isinstance(arg, int):
                raise TypeError("Function arguments must be integers")
        return func(*args, **kwargs)
    return wrapper

@validate_input
def add(x, y):
    return x + y

print(add(3, 5))  # Вывод: 8
Декоратор для кэширования результатов функции:
def memoize(func):
    cache = {}

    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]

    return wrapper

@memoize
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))  # Вывод: 55

Приведите несколько примеров генераторов, итераторов в Python.

Вот несколько примеров генераторов и итераторов в Python: Генератор для генерации последовательности чисел Фибоначчи:
def fibonacci_generator():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci_generator()
for _ in range(10):
    print(next(fib))  # Вывод: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34

Генератор для генерации бесконечной последовательности случайных чисел:

import random

def random_number_generator():
    while True:
        yield random.randint(1, 100)

rand_gen = random_number_generator()
for _ in range(10):
    print(next(rand_gen))  # Вывод: случайные числа от 1 до 100
Итератор для итерации по строке в обратном порядке:
class ReverseIterator:
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index -= 1
        return self.data[self.index]

rev_iter = ReverseIterator("hello")
for char in rev_iter:
    print(char)  # Вывод: o, l, l, e, h
Генератор для чтения файла построчно:
def read_file(filename):
    with open(filename, 'r') as file:
        for line in file:
            yield line.strip()

file_gen = read_file("example.txt")
for line in file_gen:
    print(line)

Чем итерируемый объект отличается от итератора?

Итерируемый объект умеет вернуть итератор через iter(), обычно посредством __iter__. Итератор дополнительно хранит позицию и выдает элементы через __next__, завершаясь StopIteration; его iter() обычно возвращает его самого. Коллекция может создавать новый итератор для каждого прохода, а генератор является одноразовым итератором: после исчерпания повторный цикл ничего не получит. При выборе учитывают одноразовость обхода, момент возникновения ошибки и срок жизни ресурсов, удерживаемых ленивым вычислением.


Как безопасно работать с бесконечным итератором?

Бесконечную последовательность делают генератором или через itertools.count, чтобы значения вычислялись по одному. Потребитель обязан задать границу: условие break, таймаут, лимит событий либо itertools.islice. Нельзя без ограничения передавать такой итератор в list, sum, сортировку и другие операции, ожидающие конец. Освобождение внешнего ресурса помещают в finally и при необходимости явно закрывают генератор. При выборе учитывают одноразовость обхода, момент возникновения ошибки и срок жизни ресурсов, удерживаемых ленивым вычислением.


Когда генератор лучше списка, а когда хуже?

Генератор выгоден для большого или потокового набора: он вычисляет элементы лениво и не хранит всю последовательность. Цена этого решения: нет произвольной индексации, длины, изменения элементов и бесплатного повторного прохода; ошибка может возникнуть только во время потребления. Список лучше для небольшого результата, который нужно многократно обходить, сортировать или индексировать. Генератор также может удерживать открытый ресурс до исчерпания или закрытия. При выборе учитывают одноразовость обхода, момент возникновения ошибки и срок жизни ресурсов, удерживаемых ленивым вычислением.


Зачем собственному декоратору functools.wraps?

functools.wraps(original) копирует в функцию-обертку имя, документацию, аннотации и другие метаданные, а также устанавливает __wrapped__. Это делает трассировки, документацию, inspect.signature и инструменты внедрения зависимостей понятнее. Декоратор продолжает менять поведение функции, а wraps не проверяет совместимость сигнатуры. Его ставят на внутреннюю функцию каждого обычного декоратора. Поведение также проверяют через интроспекцию реальной обертки и инструменты фреймворка, которые читают исходную сигнатуру. Это особенно важно для CLI и dependency injection.


Что делает yield from?

yield from iterable передает наружу все элементы вложенного итерируемого объекта и заменяет ручной цикл for ...: yield .... Если делегирование идет другому генератору, оператор также проксирует send, throw и close, а значение его return становится результатом выражения yield from. Для обычного списка остается только последовательная выдача элементов, без генераторного диалога. При выборе учитывают одноразовость обхода, момент возникновения ошибки и срок жизни ресурсов, удерживаемых ленивым вычислением.


Как generator.send() передает значение внутрь генератора?

После остановки на yield вызов send(value) возобновляет генератор, а переданное значение становится результатом выражения yield внутри него. Генератор выполняется до следующего yield: это следующее выданное значение возвращается из send. До первой остановки нельзя послать ненулевое значение, поэтому начинают с next(gen) или gen.send(None). Завершение по return проявляется как StopIteration. При выборе учитывают одноразовость обхода, момент возникновения ошибки и срок жизни ресурсов, удерживаемых ленивым вычислением.


Как устроен декоратор с аргументами?

Декоратор с аргументами состоит из трех вызовов: фабрика принимает настройки и возвращает декоратор; декоратор принимает исходную функцию и возвращает обертку; обертка вызывается вместо функции. Настройки сохраняются в замыкании. На обертку ставят functools.wraps. Нужно различать время импорта, когда выполняются первые два уровня, и время обычного вызова, когда выполняется обертка. При выборе учитывают одноразовость обхода, момент возникновения ошибки и срок жизни ресурсов, удерживаемых ленивым вычислением.

Собеседования: Python

Смотри записи интервью, узнай, какие вопросы задают и как отвечают кандидаты.

Вопросы и ответы

Не нашли ответ? Напишите мне в чат. Я делаю Шпаргалку и сам отвечаю на сообщения. Расскажите, что не работает или чего вам не хватает. Может, смогу сразу взять это в работу.

Откуда взяты вопросы?

Из реальных собеседований. Основой подборки стал опыт Вадима Новосёлова: он проходил интервью и записывал вопросы. Подробнее о материалах.

Насколько эти вопросы актуальны?

Эти вопросы встречались нам на реальных собеседованиях в 2025 году. Мы регулярно проходим собеседования и пополняем подборку новыми вопросами. Основы профессии и ключевые технологии остаются востребованными годами, а детали конкретных инструментов и версий стоит сверять с текущей документацией.

На какой уровень рассчитана подборка?

Мы проходили собеседования на вакансии уровня Middle+, а иногда и на Senior-позиции. Вопросы из этих интервью вошли в подборку. Направления работы: Python-разработчик, Backend-разработчик на Python. Глубина обсуждения зависит от вакансии: будь готов объяснить основную идею, привести практический пример и разобрать ограничения и альтернативы решения.

Этот вопрос точно будет на моём собеседовании?

Гарантии нет: набор вопросов зависит от компании, задач команды, уровня вакансии и самого интервьюера. Эти вопросы уже встречались на реальных собеседованиях, но на твоём интервью ту же тему могут проверить другой формулировкой, практической задачей или обсуждением твоего опыта. Используй подборку, чтобы разобраться в теме: объясняй идею своими словами, приводи примеры и готовься обсудить ограничения и альтернативы решения. Так будет проще ответить и на знакомый вопрос, и на неожиданные уточнения.