Предположим, у меня есть пакет с именем bar
, и он содержит bar.py
:
a = None
def foobar():
print a
и __ init __. py
:
from bar import a, foobar
Затем я выполняю этот сценарий:
import bar
print bar.a
bar.a = 1
print bar.a
bar.foobar()
Вот что я ожидаю:
None
1
1
Вот что я получаю:
None
1
None
Может кто-нибудь объяснить мое заблуждение?
Вы используете из импортированной полосы
. a
становится символом в глобальной области видимости импортирующего модуля (или любой другой области видимости, в которой находится оператор импорта).
Когда вы назначаете новое значение параметру a
, вы просто меняете то значение, на которое указывает a
, а не фактическое значение. Попробуйте импортировать bar.py
напрямую с помощью import bar
в __ init __. Py
и проведите там свой эксперимент, установив bar.a = 1
. Таким образом, вы фактически измените bar .__ dict __ ['a']
, который в данном контексте является «реальным» значением a
.
Он немного запутан с тремя слоями, но bar.a = 1
изменяет значение a
в модуле под названием bar
, который фактически получен из __ init __. Py
. Это не меняет значение a
, которое видит foobar
, потому что foobar
живет в реальном файле bar.py
. Вы можете установить bar.bar.a
, если хотите это изменить.
Это одна из опасностей использования формы from foo import bar
оператора import
: она разбивает bar
на два символа, один видимый глобально изнутри foo
, который начинает указывать на исходное значение и другой символ, видимый в области, где выполняется оператор import
. Изменение точки, на которую указывает символ, также не меняет значение, на которое он указывает.
Подобные вещи убивают при попытке перезагрузить
модуль из интерактивного интерпретатора.
Одна из причин трудностей с этим вопросом заключается в том, что у вас есть программа с именем bar / bar.py
: import bar
импортирует либо bar / __ init __. Py
или bar / bar.py
, в зависимости от того, где это делается, что немного затрудняет отслеживание того, a
равно bar.a
.
Вот как это работает:
Ключ к пониманию того, что происходит, - это осознать, что в вашем __ init __. Py
,
from bar import a
фактически делает что-то вроде
a = bar.a # … with bar = bar/bar.py (as if bar were imported locally from __init__.py)
и определяет новую переменную ( bar / __ init__.py:a
, если хотите). Таким образом, ваш from bar импортирует
в __ init __. Py
связывает имя bar / __ init__.py:a
с исходным bar.py:a
( Нет
). Вот почему вы можете выполнить from bar import a as a2
в __ init __. Py
: в этом случае очевидно, что у вас есть оба bar / bar.py: a
и отдельное имя переменной bar / __ init__.py:a2
(в вашем случае имена двух переменных просто совпадают с a
, но они по-прежнему находятся в разных пространствах имен: в __ init __. py
это bar.a
и a
).
Теперь, когда вы выполняете
import bar
print bar.a
, вы получаете доступ к переменной bar / __ init__.py:a
(поскольку import bar
импортирует ваш bar / __ init __. Py
]). Это переменная, которую вы изменяете (на 1). Вы не трогаете содержимое переменной bar / bar.py: a
.Поэтому, когда вы впоследствии выполняете
bar.foobar()
, вы вызываете bar / bar.py: foobar ()
, который обращается к переменной a
из bar / bar.py
, которая по-прежнему Нет
(когда foobar ()
определен, он связывает имена переменных раз и навсегда, поэтому a
в bar.py
- это bar.py:a
, а не какая-либо другая a
переменная, определенная в другом модуле - поскольку может быть много a
переменных во всех импортированных модулях). Следовательно, последний вывод None
.