Почему последовательность вызовов функций может быть некорректной и как это исправить
Что проверяют
Понимаете ли вы, когда в Python выполняется код: при импорте, при определении функции или при вызове. Формулировка размытая, и интервьюер как раз смотрит, начнёте ли вы гадать или сначала уточните, где наблюдается странный порядок: в логах, в импортах или в асинхронном коде.
Как отвечать
Разберите причины по слоям.
- время импорта: модуль исполняется сверху вниз один раз, декораторы и значения по умолчанию вычисляются в момент
def, а не при вызове. Циклический импорт даёт наполовину созданный модуль и ошибку импорта имени; - ленивость: тело генератора не выполняется до первой итерации, а вызов корутины без
awaitтолько создаёт объект, отсюда предупреждение о том, что корутину никто не ждал; - конкурентность:
asyncio.gatherсохраняет порядок результатов, но не порядок выполнения, а у потоков порядка нет вовсе; - наблюдение: порядок часто не сломан, а просто неверно виден. Вывод
printв перенаправленный файл буферизуется блоками,stderrидёт сразу, и строки в файле перемешиваются.
Дальше про инструменты: запуск с ключом -u, логирование с метками времени и именем потока вместо print, python -X importtime для разбора импортов, явная инициализация в функции вместо побочных эффектов на уровне модуля.
Где спотыкаются
Повторяют миф, что функцию нельзя вызвать раньше её определения. Внутри другой функции имя ищется во время вызова, поэтому обращение к функции, объявленной ниже по файлу, работает нормально. Ломается только вызов на уровне модуля, выполняемый до строки с def.
Второе: изменяемое значение по умолчанию. Запись def add(item, acc=[]) создаёт список один раз при определении, и он копит данные между вызовами. Правильно передавать None и создавать список внутри.
Третье: чинят порядок расстановкой пауз и перестановкой импортов. Симптом уходит до следующего запуска, а причина остаётся. Нужна либо явная зависимость через параметры и возвращаемые значения, либо ожидание готовности по признаку, а не сон наугад.