Coraz częściej używam Pythona i ciągle widzę zmienną __all__
ustawioną w różnych __init__.py
plikach. Czy ktoś może wyjaśnić, co to robi?
python
syntax
namespaces
varikin
źródło
źródło
__all__
jeśli__all__
jest obecny, nie są dokładnie ukryte; można je zobaczyć i uzyskać do nich dostęp normalnie, jeśli znasz ich nazwiska. Jedynie w przypadku „importu”, który i tak nie jest zalecany, rozróżnienie ma jakąkolwiek wagę.import *
(nptk
.). Dobrą wskazówką, jeśli tak jest, jest obecność__all__
lub nazwy zaczynające się od podkreślenia w kodzie modułu.tk
zostały wydane dzisiaj (a nawet w 2012 r.), Zalecaną praktyką byłoby użyciefrom tk import *
. Myślę, że praktyka jest akceptowana z powodu bezwładności, a nie zamierzonego projektu.Połączony z, ale nie wymieniony tutaj wyraźnie, jest dokładnie wtedy, gdy
__all__
jest używany. Jest to lista ciągów znaków określających, które symbole w module zostaną wyeksportowane, gdyfrom <module> import *
zostaną użyte w module.Na przykład poniższy kod
foo.py
jawnie eksportuje symbolebar
ibaz
:Te symbole można następnie zaimportować w następujący sposób:
Jeśli
__all__
powyższe zostanie skomentowane, kod ten zostanie wykonany do końca, ponieważ domyślnym zachowaniemimport *
jest importowanie wszystkich symboli, które nie zaczynają się znakiem podkreślenia, z podanej przestrzeni nazw.Odniesienie: https://docs.python.org/tutorial/modules.html#importing-from-a-package
UWAGA:
__all__
wpływafrom <module> import *
tylko na zachowanie. Elementy niewymienione w__all__
są nadal dostępne spoza modułu i można je importować za pomocąfrom <module> import <member>
.źródło
print(baz())
?print(baz)
drukuje coś podobnego,<function baz at 0x7f32bc363c10>
podczas gdyprint(baz())
drukujebaz
Co ma
__all__
zrobić?Deklaruje semantycznie „publiczne” nazwy z modułu. Jeśli istnieje nazwa
__all__
, użytkownicy powinni z niej korzystać i mogą oczekiwać, że się nie zmieni.Będzie także miał wpływ programowy:
import *
__all__
w module, np .module.py
:oznacza, że gdy jesteś
import *
z modułu,__all__
importowane są tylko te nazwy z :Narzędzia dokumentacji
Narzędzia do autouzupełniania dokumentacji i kodu mogą (a nawet powinny) sprawdzać także,
__all__
jakie nazwy mają być wyświetlane jako dostępne z modułu.__init__.py
zamienia katalog w pakiet PythonaZ dokumentów :
Więc
__init__.py
może zadeklarować__all__
na opakowaniu .Zarządzanie interfejsem API:
Pakiet zazwyczaj składa się z modułów, które mogą się importować, ale które są koniecznie powiązane z
__init__.py
plikiem. Ten plik sprawia, że katalog jest rzeczywistym pakietem Pythona. Załóżmy na przykład, że masz w pakiecie następujące pliki:Utwórzmy te pliki za pomocą Pythona, abyś mógł śledzić - możesz wkleić następujące elementy do powłoki Python 3:
A teraz przedstawiłeś kompletny interfejs API, którego może użyć ktoś inny podczas importowania Twojego pakietu, na przykład:
A pakiet nie będzie zawierał wszystkich innych szczegółów implementacji użytych podczas tworzenia modułów zaśmiecających
package
przestrzeń nazw.__all__
w__init__.py
Po dłuższej pracy może zdecydowałeś, że moduły są zbyt duże (jak wiele tysięcy linii?) I trzeba je rozdzielić. Więc wykonaj następujące czynności:
Najpierw utwórz katalogi podpakietowe o takich samych nazwach jak moduły:
Przenieś implementacje:
utwórz
__init__.py
s dla podpakietów, które deklarują__all__
dla każdego:A teraz masz api aprowizowany na poziomie pakietu:
I możesz łatwo dodawać do swojego API elementy, którymi możesz zarządzać na poziomie podpakietu zamiast na poziomie modułu podpakietu. Jeśli chcesz dodać nową nazwę do API, po prostu zaktualizuj
__init__.py
, np. W module_2:A jeśli nie jesteś gotowy do publikowania
Baz
w interfejsie API najwyższego poziomu, na najwyższym poziomie__init__.py
możesz mieć:a jeśli użytkownicy wiedzą o dostępności
Baz
, mogą z niej skorzystać:ale jeśli nie wiedzą o tym, inne narzędzia (takie jak pydoc ) ich nie poinformują.
Możesz później zmienić tę opcję, gdy
Baz
będzie gotowa na pierwszą porę:Prefiks
_
a__all__
:Domyślnie Python eksportuje wszystkie nazwy, które nie zaczynają się od
_
. Z pewnością mogłoby polegać na tym mechanizmie. Niektóre pakiety w standardowej biblioteki Pythona, w rzeczywistości, ma polegać na tym, ale aby to zrobić, oni alias ich importu, na przykład wctypes/__init__.py
:Korzystanie z
_
konwencji może być bardziej eleganckie, ponieważ usuwa zbędne nazewnictwo nazw ponownie. Ale dodaje redundancji dla importów (jeśli masz ich dużo) i łatwo jest zapomnieć o tym, aby robić to konsekwentnie - a ostatnią rzeczą, jakiej chcesz, jest konieczność bezterminowego wspierania czegoś, co chciałeś być jedynie szczegółem implementacji, po prostu ponieważ zapomniałeś prefiksu_
podczas nazywania funkcji.Osobiście piszę na
__all__
początku mojego cyklu rozwoju modułów, aby inni, którzy mogą używać mojego kodu, wiedzieli, czego powinni używać, a czego nie.Większość pakietów w standardowej bibliotece również używa
__all__
.Kiedy unikanie
__all__
ma sensSensowne jest trzymanie się
_
konwencji prefiksów zamiast,__all__
gdy:export
dekoratorWadą używania
__all__
jest to, że musisz wpisać nazwy funkcji i klas eksportowanych dwukrotnie - a informacje są oddzielone od definicji. My mogliśmy użyć dekorator, aby rozwiązać ten problem.Pomysł na takiego dekoratora eksportu wpadłem na pomysł Davida Beazleya na temat opakowań. Ta implementacja wydaje się działać dobrze w tradycyjnym importerze CPython. Jeśli masz specjalny hak lub system importu, nie gwarantuję go, ale jeśli go zastosujesz, wycofanie się z niego jest dość banalne - wystarczy ręcznie dodać nazwy z powrotem do
__all__
Na przykład w bibliotece narzędziowej można zdefiniować dekorator:
a następnie, tam gdzie byś zdefiniował
__all__
, robisz to:I to działa dobrze, niezależnie od tego, czy jest uruchamiane jako główne, czy importowane przez inną funkcję.
A obsługa interfejsu API również
import *
będzie działać:źródło
@export
dekoratora.__init__.py
i korzystania z__all__
__all__
poprawności.__all__
- ale wtedy powiedziałbym, że masz niestabilny API ... To byłoby coś, co wymagałoby kompleksowych testów akceptacyjnych.module_1
imodule_2
; Jest to OK, aby zawierać wyraźnądel module_1
in__init__.py
? Czy mylę się sądząc, że warto?Po prostu dodaję to dla ścisłości:
Wszystkie pozostałe odpowiedzi dotyczą modułów . Oryginalne pytanie wyraźnie wymienione
__all__
w__init__.py
plikach, więc chodzi o pakiety python .Zasadniczo
__all__
wchodzi w grę tylko wtedy, gdy używany jestfrom xxx import *
wariantimport
instrukcji. Dotyczy to zarówno pakietów, jak i modułów.Zachowanie modułów wyjaśniono w innych odpowiedziach. Dokładne zachowanie pakietów opisano tutaj szczegółowo.
Krótko mówiąc,
__all__
na poziomie pakietu robi mniej więcej to samo, co w przypadku modułów, z tym że zajmuje się modułami w pakiecie (w przeciwieństwie do określania nazw w module ).__all__
Określa więc wszystkie moduły, które zostaną załadowane i zaimportowane do bieżącej przestrzeni nazw, gdy będziemy ich używaćfrom package import *
.Duża różnica polega na tym, że jeśli pominiesz deklarację
__all__
w pakiecie__init__.py
, instrukcja wfrom package import *
ogóle nic nie zaimportuje (z wyjątkami wyjaśnionymi w dokumentacji, patrz link powyżej).Z drugiej strony, jeśli pominiesz
__all__
w module, „importowanie z gwiazdką” spowoduje zaimportowanie wszystkich nazw (nie rozpoczynających się znakiem podkreślenia) zdefiniowanych w module.źródło
from package import *
nadal zaimportuje wszystko zdefiniowane w__init__.py
, nawet jeśli nie maall
. Ważną różnicą jest to, że bez__all__
niego nie będzie automatycznie importować żadnych modułów zdefiniowanych w katalogu pakietu.Zmienia również to, co pokaże pydoc:
moduł1.py
module2.py
Moduł $ pydoc 1
$ pydoc module2
Deklaruję
__all__
we wszystkich moich modułach, a także podkreślam szczegóły wewnętrzne, które naprawdę pomagają, gdy używam rzeczy, których nigdy wcześniej nie używałeś podczas sesji tłumacza na żywo.źródło
__all__
dostosowuje*
wfrom <module> import *
__all__
dostosowuje*
wfrom <package> import *
Moduł jest
.py
plik ma być importowany.Pakiet jest katalogiem z
__init__.py
pliku. Pakiet zwykle zawiera moduły.MODUŁY
__all__
pozwala ludziom poznać „publiczne” funkcje modułu . [ @AaronHall ] Również pydoc je rozpoznaje. [ @Longpoke ]z importu modułu *
Zobacz, jak
swiss
icheddar
są wprowadzane do lokalnej przestrzeni nazw, ale niegouda
:Bez
__all__
tego byłby dostępny dowolny symbol (który nie zaczyna się od podkreślenia).Na import bez
*
nie ma wpływu__all__
moduł importu
z nazw importu modułów
zaimportuj moduł jako nazwę lokalną
PAKIETY
W
__init__.py
pliku pakietu__all__
znajduje się lista ciągów znaków z nazwami modułów publicznych lub innych obiektów. Te funkcje są dostępne do importowania symboli wieloznacznych. Podobnie jak w przypadku modułów,__all__
dostosowuje*
przy importowaniu symboli wieloznacznych z pakietu. [ @MartinStettner ]Oto fragment Python MySQL Connector
__init__.py
:Domyślny przypadek, gwiazdka bez
__all__
dla pakietu , jest skomplikowany, ponieważ oczywiste zachowanie byłoby kosztowne: użycie systemu plików do wyszukiwania wszystkich modułów w pakiecie. Zamiast tego podczas czytania dokumentów__init__.py
importowane są tylko obiekty zdefiniowane w :[ PEP 8 , @ToolmakerSteve]
źródło
from <package> import *
nie__all__
w__init__.py
który jest nie lada importowania modułów .__init__.py
był modułem . Ale nie jestem pewien, czy to jest dokładne, w szczególności jeśli wykluczone są obiekty z prefiksem podkreślenia. Ponadto jaśniej oddzieliłem sekcje dotyczące MODUŁÓW i PAKIETÓW. Twoje myśli?Z (Nieoficjalna) Wiki Python Wiki :
źródło
__all__
służy do dokumentowania publicznego interfejsu API modułu Python. Chociaż jest to opcjonalne,__all__
należy go użyć.Oto odpowiedni fragment odwołania do języka Python :
PEP 8 używa podobnego sformułowania, chociaż wyjaśnia również, że zaimportowane nazwy nie są częścią publicznego interfejsu API, gdy
__all__
jest nieobecny:Ponadto, jak wskazano w innych odpowiedziach,
__all__
umożliwia importowanie symboli wieloznacznych dla pakietów :źródło
Krótka odpowiedź
__all__
wpływa nafrom <module> import *
stwierdzenia.Długa odpowiedź
Rozważ ten przykład:
W
foo/__init__.py
:(Implikowany) Jeśli nie zdefiniujemy
__all__
, tofrom foo import *
zaimportujemy tylko nazwy zdefiniowane wfoo/__init__.py
.(Jawne) Jeśli zdefiniujemy
__all__ = []
, tofrom foo import *
nic nie zaimportujemy.(Jawne) Jeśli zdefiniujemy
__all__ = [ <name1>, ... ]
, wówczasfrom foo import *
zaimportujemy tylko te nazwy.Zauważ, że w domyślnym przypadku python nie importuje nazw zaczynających się od
_
. Możesz jednak wymusić importowanie takich nazw przy użyciu__all__
.Można wyświetlić dokument Pythona tutaj .
źródło
__all__
wpływa na to, jakfrom foo import *
działa.Kod znajdujący się w treści modułu (ale nie w treści funkcji lub klasy) może używać gwiazdki
*
wfrom
instrukcji:Te
*
wnioski, że wszystkie atrybuty modułufoo
(z wyjątkiem początku podkreślenia) jest związany jako zmienne globalne w module przywozu. Gdyfoo
ma atrybut__all__
, wartością atrybutu jest lista nazw powiązanych tym typemfrom
instrukcji.Jeśli
foo
jest to pakiet i jego__init__.py
definiuje listę nazwie__all__
, to przyjmuje się lista nazw modułem, który powinien być importowane przyfrom foo import *
napotkaniu. Jeśli__all__
nie jest zdefiniowane, instrukcjafrom foo import *
importuje dowolne nazwy zdefiniowane w pakiecie. Obejmuje to wszelkie nazwy zdefiniowane (i jawnie załadowane podmoduły) przez__init__.py
.Pamiętaj, że
__all__
nie musi to być lista. Zgodnie z dokumentacjąimport
instrukcji , jeśli jest zdefiniowana,__all__
musi to być ciąg ciągów, które są nazwami zdefiniowanymi lub zaimportowanymi przez moduł. Więc równie dobrze możesz użyć krotki, aby zaoszczędzić trochę pamięci i cykli procesora. Nie zapomnij o przecinku, jeśli moduł definiuje jedną nazwę publiczną:Zobacz także Dlaczego „import *” jest zły?
źródło
Jest to zdefiniowane w PEP8 tutaj :
PEP8 zapewnia konwencje kodowania dla kodu Pythona zawierającego standardową bibliotekę w głównej dystrybucji Pythona. Im więcej tego przestrzegasz, tym bardziej zbliżasz się do pierwotnych zamiarów.
źródło