Czy mogę użyć __init__.py do definiowania zmiennych globalnych?

130

Chcę zdefiniować stałą, która powinna być dostępna we wszystkich podmodułach pakietu. Pomyślałem, że najlepszym miejscem będzie __init__.pyplik pakietu głównego. Ale nie wiem, jak to zrobić. Załóżmy, że mam kilka podpakietów, z których każdy zawiera kilka modułów. Jak mogę uzyskać dostęp do tej zmiennej z tych modułów?

Oczywiście, jeśli jest to całkowicie błędne i istnieje lepsza alternatywa, chciałbym to wiedzieć.

Andrei Vajna II
źródło

Odpowiedzi:

196

Powinieneś móc je włożyć __init__.py. Odbywa się to cały czas.

mypackage/__init__.py:

MY_CONSTANT = 42

mypackage/mymodule.py:

from mypackage import MY_CONSTANT
print "my constant is", MY_CONSTANT

Następnie zaimportuj mymodule:

>>> from mypackage import mymodule
my constant is 42

Mimo to, jeśli masz stałe, rozsądne byłoby (prawdopodobnie najlepsze praktyki) umieszczenie ich w oddzielnym module (constants.py, config.py, ...), a następnie, jeśli chcesz je umieścić w przestrzeni nazw pakietu, zaimportuj im.

mypackage/__init__.py:

from mypackage.constants import *

Mimo to nie obejmuje to automatycznie stałych w przestrzeniach nazw modułów pakietu. Każdy z modułów w pakiecie nadal będzie musiał jawnie importować stałe z mypackagelub z mypackage.constants.

Jason R. Coombs
źródło
19
To powinna być akceptowana odpowiedź. Jeśli pracujesz z Pythonem 2.5 lub nowszym, możesz również użyć jawnego importu względnego, jak opisano tutaj : from . import MY_CONSTANT
ThatAintWorking
czy drugi sposób nie działa tylko dla stałych? from mypackage.constants import *MY_CONSTANT
umieści
@hardmooth: Niezupełnie. Wartości są kopiowane przez odniesienie, więc jeśli miałbyś dokonać mutacji MY_CONSTANTw którymkolwiek z modułów, zmutowałoby to wszędzie. Jeśli miałbyś ponownie przypisać MY_CONSTANTw którymkolwiek z modułów, wpłynęłoby to tylko na ten moduł. Jeśli taki jest Twój zamiar, musisz odwoływać się za pomocą atrybutu, tj mypackage.constants.MY_CONSTANT.
Jason R. Coombs,
7
Jeden haczyk z przykładu jest jeśli importować mymodule.pyw __init__.pywcześniej MY_CONSTANT = 42, że nie uda, ponieważ podczas importowania mymodule.py MY_CONSTANTnie została jeszcze określona. Więc trzeba iść MY_CONSTANT = 42wyżejimport mymodule
markmnl
a co ze zmienną? Pyta o zmienne, a nie o stałe. Jak Python traktuje zmienne wewnątrz pakietu? Co jeśli zmienna w pakiecie została zmieniona?
TomSawyer
31

Nie możesz tego zrobić. Będziesz musiał jawnie zaimportować swoje stałe do przestrzeni nazw każdego modułu. Najlepszym sposobem na osiągnięcie tego jest zdefiniowanie stałych w module „config” i zaimportowanie ich wszędzie tam, gdzie jest to potrzebne:

# mypackage/config.py
MY_CONST = 17

# mypackage/main.py
from mypackage.config import *
Ferdinand Beyer
źródło
Tak, plik konfiguracyjny jest tym, co chciałbym. Po prostu pomyślałem, że init .py będzie dobrym miejscem. Twoje rozwiązanie brzmi jak standardowa praktyka. Czy to jest?
Andrei Vajna II
1
Słuszna uwaga. Nie zdawałem sobie sprawy, że chodzi o to, aby stałe były automatycznie umieszczane w przestrzeni nazw wszystkich modułów pakietu.
Jason R. Coombs
Ale za każdym razem, gdy skrypt importuje plik config.py, wykonywany jest kod wewnątrz. Co radzisz, jeśli kod wewnątrz config.py ma być uruchomiony tylko raz? Powiedzmy, że czytam plik settings.json w config.py i nie chcę go otwierać () za każdym razem, gdy importuję config.py.
Augiwan
@UGS Tak nie działa Python. Każdy moduł jest wykonywany tylko raz. Gdy jest importowany po raz drugi, moduł jest już zapisany w pamięci podręcznej sys.modules.
Ferdinand Beyer
@FerdinandBeyer Ups! Zapomniałem wspomnieć, że importuję config.py z kilku skryptów, a nie z tego samego skryptu. Powiedzmy, że a.py importuje config.py & b.py, a b.py importuje config.py. Zastanawiałem się, czy można upewnić się, że kod wewnątrz config.py jest wykonywany tylko raz.
Augiwan
2

Możesz definiować zmienne globalne z dowolnego miejsca, ale to naprawdę zły pomysł. importuj __builtin__moduł i modyfikuj lub dodawaj atrybuty do tych modułów, a nagle masz nowe wbudowane stałe lub funkcje. W rzeczywistości, kiedy moja aplikacja instaluje gettext, otrzymuję funkcję _ () we wszystkich moich modułach, bez importowania czegokolwiek. Jest to więc możliwe, ale oczywiście tylko dla projektów typu aplikacji, a nie dla pakietów lub modułów wielokrotnego użytku.

I chyba nikt nie poleciłby tej praktyki. Co jest nie tak z przestrzenią nazw? Wspomniana aplikacja posiada moduł wersji, tak że mam „globalne” zmienne dostępne jak version.VERSION, version.PACKAGE_NAMEetc.

u0b34a0f6ae
źródło
0

Chciałem tylko dodać, że stałe można zastosować za pomocą pliku config.ini i przeanalizować je w skrypcie przy użyciu biblioteki configparser. W ten sposób możesz mieć stałe dla wielu okoliczności. Na przykład, jeśli masz stałe parametrów dla dwóch oddzielnych żądań adresu URL, po prostu oznacz je w ten sposób:

mymodule/config.ini
[request0]
conn = 'admin@localhost'
pass = 'admin'
...

[request1]
conn = 'barney@localhost'
pass = 'dinosaur'
...

Dokumentacja na stronie Pythona okazała się bardzo pomocna. Nie jestem pewien, czy są jakieś różnice między Pythonem 2 i 3, więc oto linki do obu:

Python 3: https://docs.python.org/3/library/configparser.html#module-configparser

Python 2: https://docs.python.org/2/library/configparser.html#module-configparser

Dan Temkin
źródło