Итераторы, генераторы, и декораторы
Что такое декоратор?
Декоратор в 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().
Ссылки для изучения
Примеры хороших ответов из реальных собеседований
- Собеседование Senior Python-инженера: backend, данные и архитектура · 4:48–7:00Техническое собеседование · Ответ кандидата
Кандидат объясняет назначение декоратора и реализацию обёртки в Python: принять исходный объект и вернуть объект с дополнительным поведением.
- Мок-собеседование Senior Python-разработчика · 17:28–18:38Мок-собеседование · Ответ кандидата
Что происходит при применении @декоратора: вызов callable и присваивание результата имени декорируемой функции.
Чем отличается декоратор от итератора?
Декоратор и итератор - это два различных концепта в 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. Для обычного списка остается только последовательная выдача элементов, без генераторного диалога. При выборе учитывают одноразовость обхода, момент возникновения ошибки и срок жизни ресурсов, удерживаемых ленивым вычислением.
Ссылки для изучения
Примеры хороших ответов из реальных собеседований
- Мок-собеседование на Middle Python Backend-разработчика · 11:13–11:51Мок-собеседование · Ответ кандидата
Кандидат объясняет yield from как передачу управления подгенератору и канал между вызывающим кодом и вложенным генератором или итератором.
Как generator.send() передает значение внутрь генератора?
После остановки на yield вызов send(value) возобновляет генератор, а переданное значение становится результатом выражения yield внутри него. Генератор выполняется до следующего yield: это следующее выданное значение возвращается из send. До первой остановки нельзя послать ненулевое значение, поэтому начинают с next(gen) или gen.send(None). Завершение по return проявляется как StopIteration. При выборе учитывают одноразовость обхода, момент возникновения ошибки и срок жизни ресурсов, удерживаемых ленивым вычислением.
Ссылки для изучения
Примеры хороших ответов из реальных собеседований
- Senior Python-собеседование: GIL, Kafka и ETL · 6:40–6:59Собеседование · Ответ кандидата
Кандидат объясняет, что значение из send попадает в переменную слева от выражения yield, когда генератор возобновляет выполнение.
Как устроен декоратор с аргументами?
Декоратор с аргументами состоит из трех вызовов: фабрика принимает настройки и возвращает декоратор; декоратор принимает исходную функцию и возвращает обертку; обертка вызывается вместо функции. Настройки сохраняются в замыкании. На обертку ставят functools.wraps. Нужно различать время импорта, когда выполняются первые два уровня, и время обычного вызова, когда выполняется обертка. При выборе учитывают одноразовость обхода, момент возникновения ошибки и срок жизни ресурсов, удерживаемых ленивым вычислением.
Ссылки для изучения







