Страницы

Поиск по вопросам

суббота, 8 февраля 2020 г.

Рекурсивный импорт в Python

#python #рекурсия


Просьба подробно объяснить мне глупому поведение циклического импорта из книги

Марка Лутца # Изучаем Python [4-ое издание]:

Часть 5. Модули >> Упражнение 7. Циклический импорт:

# recur1.py

X = 1

import recur2

Y = 2


# recur2.py

from recur1 import X

from recur1 import Y


В зависимости от порядка импорта каждого файла и прямого их запуска в интерактивной
оболочке. Итого = 4 ситуации.

Всю голову себе изломал, но никак не въеду.
    


Ответы

Ответ 1



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, поэтому ее и нет. Если допустил какие-то неточности, заранее прошу прощения. Ответ получился большим, мог и упустить что-то.

Ответ 2



Я аналогично предварительно экспериментировал с трассировкой, но объяснения Лутцом рекурсивного импорта порядком меня запутали. А потом всё встало на свои места: Создание объекта модуля инструкцией import происходит лишь единожды, далее - интерпретатор пропускает все последующие вызовы import этого модуля и занимается выполнением иных инструкций с дополнением пространства имён. Инструкция from является расширенной версией инструкции import с дополнительной операцией копирования имён импортируемого модуля в пространство имён импортирующего модуля. Кстати, решение Лутца Решение? Не используйте инструкцию from в операции рекурсивного импорта (в самом деле!). Интерпретатор не зациклится, если вы все-таки сделаете это, но ваша программа попадет в зависимость от порядка следования инструкций в модулях. ... Если от циклов не удается избавиться полностью, попробуйте отсрочить обращение к именам модуля, используя инструкции import и полные имена (вместо инструкции from), 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') import recur1 # Вместо from recur1 import x, y print('x =', recur1.x) print('y =', recur1.y) не актуально: >>> import recur1 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 AttributeError: 'module' object has no attribute 'y' Спасибо за развёрнутый ответ! Отблагодарить репутация на сайте не позволяет. (Голос за requires 15 reputation.)

Комментариев нет:

Отправить комментарий