Вы используете from bar import a
. a
становится символом в глобальной области видимости импортирующего модуля (или любой другой области, в которой находится оператор импорта).
Когда вы присваиваете новое значение a
, вы просто меняете то значение, которое a
указывает, а не фактическое значение. Попробуйте импортировать bar.py
напрямую через import bar
in __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
инструкция. Изменение точки, на которую указывает символ, также не меняет значение, на которое он указывает.
Подобные вещи - убийца, когда пытаются reload
получить модуль из интерактивного интерпретатора.
bar.bar.a
подход в большинстве случаев не поможет. Вероятно, это слабая идея смешивать компиляцию или загрузку модуля и назначения времени выполнения, потому что вы в конечном итоге запутаете себя (или других, которые используют ваш код). Особенно в языках, которые ведут себя несколько непоследовательно, как, возможно, делает python.Один источник трудностей с этим вопросом является то , что у вас есть программа под названием
bar/bar.py
:import bar
импорт либоbar/__init__.py
илиbar/bar.py
, в зависимости от того, где это делается, что делает его немного громоздким , чтобы отслеживать , какиеa
естьbar.a
.Вот как это работает:
Ключ к пониманию того, что происходит, чтобы понять , что в вашем
__init__.py
,по сути делает что-то вроде
и определяет новую переменную (
bar/__init__.py:a
если хотите). Таким образом, вашfrom bar import a
in__init__.py
связывает имяbar/__init__.py:a
с исходнымbar.py:a
объектом (None
). Именно поэтому вы можете сделатьfrom bar import a as a2
в__init__.py
: в этом случае, очевидно , что у вас есть какbar/bar.py:a
и в отчетливую имя переменнойbar/__init__.py:a2
(в вашем случае, имена двух переменных просто оказались обаa
, но они по- прежнему живут в разных пространствах имен: в__init__.py
, они естьbar.a
иa
).Теперь, когда вы это сделаете
вы обращаетесь к переменной
bar/__init__.py:a
(посколькуimport bar
импортирует вашуbar/__init__.py
). Это переменная, которую вы изменяете (на 1). Вы не трогаете содержимое переменнойbar/bar.py:a
. Итак, когда вы впоследствии сделаетевы вызываете
bar/bar.py:foobar()
, который обращается к переменнойa
изbar/bar.py
, которая по-прежнемуNone
(когдаfoobar()
определена, она связывает имена переменных раз и навсегда, так чтоa
inbar.py
естьbar.py:a
, а не любая другаяa
переменная, определенная в другом модуле, поскольку может быть многоa
переменных во всех импортированных модулях ). Отсюда последнийNone
вывод.Вывод: лучше избегать двусмысленности
import bar
, не имеяbar/bar.py
модуля (посколькуbar.__init__.py
каталогbar/
уже становится пакетом, с которым вы также можете импортироватьimport bar
).источник
Иными словами: оказывается, это заблуждение очень легко сделать. Это хитроумно определено в справочнике по языку Python: использование объекта вместо символа . Я бы посоветовал, чтобы справочник по языку Python сделал это более ясным и менее разреженным.
ТЕМ НЕ МЕНИЕ:
При импорте вы импортируете текущее значение импортированного символа и добавляете его в свое пространство имен, как определено. Вы не импортируете ссылку, вы фактически импортируете значение.
Таким образом, чтобы получить обновленное значение
i
, вы должны импортировать переменную, содержащую ссылку на этот символ.Другими словами, импорт НЕ похож на
import
JAVA,external
объявление в C / C ++ или дажеuse
предложение в PERL.Скорее, следующий оператор в Python:
больше похож на следующий код в K&R C:
(предостережение: в случае Python «a» и «x» по сути являются ссылкой на фактическое значение: вы не копируете INT, вы копируете ссылочный адрес)
источник
import
намного чище, чем Java, потому что пространства имен / области всегда должны быть должным образом разделены и никогда не мешают друг другу неожиданным образом. Как здесь: изменение привязки объекта к имени в пространстве имен (читай: присвоение чего-либо глобального свойства модуля) никогда не влияет на другие пространства имен (читай: ссылка, которая была импортирована) в Python. Но делает это в Java и т. Д. В Python вам нужно только понимать, что импортируется, в то время как в Java вы также должны понимать другой модуль на случай, если он изменит это импортированное значение позже.