Używasz from bar import a
. a
staje się symbolem w globalnym zakresie modułu importującego (lub jakimkolwiek zakresie, w którym występuje instrukcja import).
Kiedy przypisujesz nową wartość do a
, zmieniasz tylko a
punkty wartości , a nie rzeczywistą wartość. Spróbuj zaimportować bar.py
bezpośrednio z import bar
in __init__.py
i przeprowadź tam eksperyment, ustawiając bar.a = 1
. W ten sposób faktycznie będziesz modyfikować, bar.__dict__['a']
co jest „prawdziwą” wartością a
w tym kontekście.
Jest trochę zagmatwany z trzema warstwami, ale bar.a = 1
zmienia wartość a
w module o nazwie, z bar
którego faktycznie pochodzi __init__.py
. Nie zmienia wartości tego, a
co foobar
widzi, ponieważ znajduje się foobar
w aktualnym pliku bar.py
. Możesz ustawić, bar.bar.a
jeśli chcesz to zmienić.
Jest to jedno z niebezpieczeństw stosowania from foo import bar
formy import
instrukcji: dzieli się ona bar
na dwa symbole, jeden widoczny globalnie od wewnątrz, od foo
którego zaczyna się wskazanie pierwotnej wartości i inny symbol widoczny w zakresie, w którym import
instrukcja jest wykonywana. Zmiana punktu, w którym symbol wskazuje, nie zmienia również wskazywanej wartości.
Tego rodzaju rzeczy są zabójcze, gdy próbujesz przejść reload
do modułu z interaktywnego interpretera.
bar.bar.a
podejście nie pomoże w większości przypadków użycia. Prawdopodobnie jest to słaby pomysł, aby mieszać kompilację lub ładowanie modułu i przypisania w czasie wykonywania, ponieważ w końcu będziesz mylić siebie (lub innych, którzy używają twojego kodu). Zwłaszcza w językach, które zachowują się nieco niekonsekwentnie, jak prawdopodobnie Python.Jednym ze źródeł trudności z tym pytaniem jest to, że masz program o nazwie
bar/bar.py
:import bar
importu albobar/__init__.py
albobar/bar.py
, w zależności od tego, gdzie to jest zrobione, co sprawia, że jest to trochę kłopotliwe, aby śledzić, którea
jestbar.a
.Oto jak to działa:
Kluczem do zrozumienia tego, co się dzieje, aby uświadomić sobie, że w twojej
__init__.py
,w efekcie robi coś podobnego
i definiuje nową zmienną (
bar/__init__.py:a
jeśli chcesz). W ten sposób twójfrom bar import a
in__init__.py
wiąże nazwębar/__init__.py:a
z oryginalnymbar.py:a
obiektem (None
). Dlatego możesz to zrobićfrom bar import a as a2
w__init__.py
: w tym przypadku jest jasne, że masz obiebar/bar.py:a
i odrębną nazwę zmiennejbar/__init__.py:a2
(w twoim przypadku nazwy dwóch zmiennych po prostu sąa
, ale nadal istnieją w różnych przestrzeniach nazw: w__init__.py
, sąbar.a
ia
).Teraz, kiedy to zrobisz
uzyskujesz dostęp do zmiennej
bar/__init__.py:a
(ponieważimport bar
importuje twojąbar/__init__.py
). To jest zmienna, którą modyfikujesz (do 1). Nie dotykasz zawartości zmiennejbar/bar.py:a
. Więc kiedy później to zrobiszwywołujesz
bar/bar.py:foobar()
, która uzyskuje dostęp do zmienneja
zbar/bar.py
, która jest nadalNone
(gdyfoobar()
jest zdefiniowana, wiąże nazwy zmiennych raz na zawsze, więca
inbar.py
jestbar.py:a
, a nie żadna innaa
zmienna zdefiniowana w innym module - ponieważ może być wielea
zmiennych we wszystkich zaimportowanych modułach ). Stąd ostatnieNone
wyjście.Wniosek: najlepiej jest uniknąć wszelkich niejasności
import bar
, nie mając żadnegobar/bar.py
modułu (ponieważbar.__init__.py
sprawia, że katalogbar/
jest już pakietem, za pomocą którego można również importowaćimport bar
).źródło
Innymi słowy: okazuje się, że to błędne przekonanie jest bardzo łatwe do zrobienia. Jest to podstępnie zdefiniowane w odwołaniu do języka Python: użycie obiektu zamiast symbolu . Sugerowałbym, aby odniesienie do języka Python uczyniło to bardziej przejrzystym i mniej rzadkim.
JEDNAK:
Podczas importu importujesz bieżącą wartość importowanego symbolu i dodajesz ją do swojej przestrzeni nazw zgodnie z definicją. Nie importujesz odniesienia, efektywnie importujesz wartość.
Dlatego, aby uzyskać zaktualizowaną wartość
i
, musisz zaimportować zmienną, która zawiera odniesienie do tego symbolu.Innymi słowy, import NIE jest taki jak
import
w JAVA,external
deklaracja w C / C ++ ani nawetuse
klauzula w PERL.Raczej poniższa instrukcja w Pythonie:
jest bardziej podobny do następującego kodu w K&R C:
(uwaga: w przypadku Pythona „a” i „x” są zasadniczo odniesieniem do rzeczywistej wartości: nie kopiujesz INT, tylko kopiujesz adres odniesienia)
źródło
import
znacznie bardziej przejrzysty niż Java, ponieważ przestrzenie / zakresy nazw powinny być zawsze odpowiednio oddzielone i nigdy nie kolidują ze sobą w nieoczekiwany sposób. Tak jak tutaj: zmiana powiązania obiektu z nazwą w przestrzeni nazw (czytaj: przypisanie czegoś do właściwości globalnej modułu) nigdy nie wpływa na inne przestrzenie nazw (czytaj: referencję, która została zaimportowana) w Pythonie. Ale robi to w Javie itp. W Pythonie wystarczy zrozumieć, co jest importowane, podczas gdy w Javie należy również zrozumieć inny moduł, na wypadek gdyby zmienił on później importowaną wartość.