Напиши контекстный менеджер, который замерит время выполнения функции в Python
Что проверяют
Знание протокола менеджера контекста и умение выбрать правильный таймер. Смотрят, помните ли вы сигнатуру __exit__, знаете ли короткий путь через contextlib, и понимаете ли, чем perf_counter отличается от time.time.
Как отвечать
Короткий вариант через декоратор генератора:
import time
from contextlib import contextmanager
@contextmanager
def timer(label="block"):
start = time.perf_counter()
try:
yield
finally:
print(f"{label}: {time.perf_counter() - start:.4f}")Класс нужен, когда замер надо прочитать после блока:
class Timer:
def __enter__(self):
self.start = time.perf_counter()
return self
def __exit__(self, exc_type, exc, tb):
self.elapsed = time.perf_counter() - self.start
return FalseОбязательно объясните выбор таймера. perf_counter монотонный, он не зависит от перевода системных часов и от синхронизации времени по сети, поэтому интервалы меряют им. time.time это календарное время, оно может уехать назад прямо посреди замера. Для процессорного времени без ожиданий ввода-вывода есть process_time, а наследование от ContextDecorator позволяет применять ту же обёртку и как with, и как декоратор функции.
Где спотыкаются
Меряют через time.time. На одном прогоне разница незаметна, но это неверный инструмент для интервалов, и внимательный интервьюер спросит именно про это.
Забывают try и finally в генераторной версии. Если код внутри блока бросил исключение, до строки с выводом дело не дойдёт, и замер потеряется ровно в том случае, когда он нужнее всего.
Возвращают True из __exit__. Истинное значение подавляет исключение, и менеджер контекста, написанный ради замера времени, начинает молча глотать ошибки вызывающего кода. Возвращать нужно False или вообще ничего.