Как изменить переменную модуля из другого модуля?


Предположим, у меня есть пакет с именем 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

может ли кто-нибудь объяснить мое заблуждение?

3 69

3 ответа:

вы используете from bar import a. 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 инструкция выполняется. Изменение a там, где символ указывает, не изменяет значение, на которое он указал.

такого рода вещи являются убийцей при попытке reload модуль из интерактивного интерпретатора.

одним из источников трудностей с этим вопросом является то, что у вас есть программа с именем 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 import a на __init__.py персонализация имя bar/__init__.py:a в оригинале

поставить другим способом: Оказывается, это заблуждение очень легко сделать. это исподтишка, определенные в языке Python справочник по языку: использование объект вместо символ. Я бы предложил, чтобы ссылка на язык Python сделала это более ясным и менее разреженным..

The from форма не связывает имя модуля: он проходит через список идентификаторов, ищет каждый из них в модуле, найденном в шаг (1), и связывает имя в локальном пространстве имен с объект таким образом найдено.

:

при импорте вы импортируете текущее значение импортированного символа и добавляете его в пространство имен, как определено.вы не импортируете ссылку, вы фактически импортируете значение.

таким образом, чтобы получить обновленное значение i, необходимо импортировать переменную, содержащую ссылку к этому символу.

другими словами, импорт не похож на import в JAVA,external объявление в C / C++ или даже a use предложение в PERL.

скорее, следующее утверждение в Python:

from some_other_module import a as x

больше как следующий код в K&R C:

extern int a; /* import from the EXTERN file */

int x = a;

(предостережение: в случае Python "a" и " x " по существу являются ссылкой на фактическое значение: вы не копируете INT, вы копируете ссылочный адрес)