
Снова сходив на несколько собеседований и пройдя тестовые задания, я заметил, что интервьюерам нравятся задания наподобие следующего.
def f(x, l=[]): for i in range(x): l.append(i * i) return l >>> f(2) >>> f(3, [0, 1, 2]) >>> f(3)
Вывод первых двух строк достаточно очевиден, однако результат выполнения третьей строки f(3) не показался мне таким однозначным. Давайте посмотрим, что происходит после инициализации функции f. Чтобы запустить этот код, я воспользуюсь IPython.
>>> f <function __main__.f(x, l=[])> >>> f.__defaults__ ([],)
Пустой список, который мы видим из результата выполнения f.__defaults__ — это переменная l в коде функции.
>>> f(2) [0, 1]
Ничего особенного.
>>> f <function __main__.f(x, l=[0, 1])> >>> f.__defaults__ ([0, 1],)
Однако! Теперь мы видим, что переменная l имеет значение [0, 1] в силу изменчивости объекта списка в Python и передачи аргументов функции в качестве ссылки.
>>> f(3, [0, 1, 2]) [0, 1, 2, 0, 1, 4] >>> f <function __main__.f(x, l=[0, 1])>
Тоже ничего особенного. Просто передача объекта list в качестве переменной l.
>>> f(3) [0, 1, 0, 1, 4] >>> f <function __main__.f(x, l=[0, 1, 0, 1, 4])>
А вот теперь самое интересное. Когда вы запускаете f(3), Python не использует пустой список, который определен в коде функции, он использует переменную l со значениями из f.__defaults__ ([0, 1]).
P.S.
Если вам нужна функция, которая использует пустой список после каждого вызова, вам следует использовать что-то вроде этого (установить значение ‘l’ в ‘None’).
def f(x, l=None): if l is None: l = [] for i in range(x): l.append(i * i) return l >>> f(2) [0, 1] >>> f(3, [0, 1, 2]) [0, 1, 2, 0, 1, 4] >>> f(3) [0, 1, 4]
Заключение
Вот я и разобрал одну из самых популярных тестовых задач на собеседовании. Этот пост призван показать, что вы не всегда можете полагаться на свою интуицию, впрочем, как и я на свою :-).
Надеемся данный перевод станет полезным для вас. Традиционно ждем комментарии и до встречи на курсе.
ссылка на оригинал статьи https://habr.com/ru/company/otus/blog/459518/
Добавить комментарий