На 27 Django Moscow Meetup Николай Чабановский рассказывал про Stackoverflow на русском.

Написал ответ на вопрос, ответ получился слишком обширным, на правах автора забираю сюда.

Вопрос был про рекурсивный импорт в Python.

recur1.py:

print('x = 1, hi from recur1! We are in recur1 file')
x = 1

print('import recur2... We are in recur1 file')
import recur2

print('y = 2, hi from recur1! We are in recur1 file')
y = 2

recur2.py:

print('import x from recur1... We are in recur2 file')
from recur1 import x

print('import y from recur1... We are in recur2 file')
from recur1 import y

print('hi from recur2! We are in recur2 file')

В ходе импортирования инструкции в файле выполняются сначала и до конца.

Если выполним следующее:

>>> import recur1

То получим ошибку:

ImportError: cannot import name 'y'

Это происходит по очень простой причине. В модуле recur1 сначала создается переменная x, а потом сразу импортируется recur2, в котором мы используем from.

Таким образом, у нас будет доступ только к тем именам, которые уже были определены в этом модуле. То есть доступ только к x, y не будет доступен, так как к нему будет присвоено значение после импорта в recur1.

from recur1 import y # а у еще нет!

Лучше не использовать при рекурсивном импорте инструкцию from. При рекурсивном импорте интерпретатор не будет повторно выполнять инструкции.

Это легко можно проверить:

>>> import recur2
import x from recur1... We are in recur2 file
x = 1, hi from recur1! We are in recur1 file
import recur2... We are in recur1 file
y = 2, hi from recur1! We are in recur1 file
import y from recur1... We are in recur2 file
hi from recur2! We are in recur2 file

Ошибки не происходит, потому что при первом импорте мы получаем x, потом снова импортируем из recur1 recur2, повторного определения переменной х происходить не будет (см. выше), поэтому сразу получим y, и когда уже захотим импортнуть его из recur2, также повторно инструкции не будут выполняться.

Идем дальше:

>>> from sys import modules
>>> 'recur1' in modules.keys()
False
>>> 'recur2' in modules.keys()
False
>>> import recur2
# вывод см. выше
>>> 'recur2' in modules.keys()
True
>>> 'recur1' in modules.keys()
True

И когда после этого мы захотим сразу сделать:

>>> import recur1

То ошибки в

from recur1 import y

не будет, так как y уже будет объявлен в recur1:

>>> help(modules['recur1'])

Вернет:

NAME
    recur1

DATA
    x = 1
    y = 2

Если же запустим

python recur1.py

Получим:

x = 1, hi from recur1! We are in recur1 file
import recur2... We are in recur1 file
import x from recur1... We are in recur2 file
x = 1, hi from recur1! We are in recur1 file
import recur2... We are in recur1 file
y = 2, hi from recur1! We are in recur1 file
import y from recur1... We are in recur2 file
hi from recur2! We are in recur2 file
y = 2, hi from recur1! We are in recur1 file

Как пишет Лутц:

Когда модули запускаются как самостоятельные программы, они не импортируются, поэтому здесь возникает тот же эффект, как и при импортировании recur2 в интерактивной оболочке, – recur2 является первым импортируемым модулем.

А при

python recur2.py

В выводе увидим:

import x from recur1... We are in recur2 file
x = 1, hi from recur1! We are in recur1 file
import recur2... We are in recur1 file
import x from recur1... We are in recur2 file
import y from recur1... We are in recur2 file
ImportError: cannot import name 'y'

То есть мы еще не объявили в recur1 переменную y, поэтому ее и нет.