Czy ktoś może wyjaśnić wszystko w Pythonie?
Używam Pythona coraz częściej i ciągle widzę zmienną __all__
ustawioną w różnych plikach __init__.py
. Czy ktoś może wyjaśnić, co to robi?
5 answers
Jest to lista obiektów publicznych tego modułu, interpretowana przez import *
. Zastępuje domyślne ukrywanie wszystkiego, co zaczyna się od podkreślenia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-08 15:50:12
Linked to, ale nie jest tu wyraźnie wymienione, jest dokładnie wtedy, gdy __all__
jest używany. Jest to lista łańcuchów określających, jakie symbole w module zostaną wyeksportowane, gdy from <module> import *
zostanie użyta w module.
Na przykład następujący kod w foo.py
wyraźnie eksportuje symbole bar
i baz
:
__all__ = ['bar', 'baz']
waz = 5
bar = 10
def baz(): return 'baz'
Te symbole mogą być importowane w następujący sposób:
from foo import *
print bar
print baz
# The following will trigger an exception, as "waz" is not exported by the module
print waz
Jeśli __all__
powyżej zostanie skomentowany, kod ten zostanie wykonany do końca, ponieważ domyślnym zachowaniem import *
jest importowanie z podanej przestrzeni nazw wszystkich symboli, które nie rozpoczynają się podkreśleniem.
Numer referencyjny: https://docs.python.org/3.5/tutorial/modules.html#importing-from-a-package
Uwaga: __all__
wpływa tylko na zachowanie from <module> import *
. Elementy, które nie są wymienione w __all__
, są nadal dostępne spoza modułu i mogą być importowane za pomocą from <module> import <member>
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-14 09:35:53
Dodaję to dla ścisłości:
Wszystkie pozostałe odpowiedzi odnoszą się do modułów. Oryginalne pytanie wyraźnie wspomniało __all__
w plikach __init__.py
, więc chodzi o Pakiety Pythona .
Ogólnie rzecz biorąc, __all__
wchodzi w grę tylko wtedy, gdy from xxx import *
wariant import
jest używany. Dotyczy to zarówno pakietów, jak i modułów.
Zachowanie modułów zostało wyjaśnione w pozostałych odpowiedziach. Dokładne zachowanie pakietów jest opisane tutaj w szczegółach.
W skrócie, __all__
na poziomie pakietu robi mniej więcej to samo co w przypadku modułów, z tym że zajmuje się modułami wewnątrz pakietu (w przeciwieństwie do określania nazw wewnątrz modułu ). So __all__
określa wszystkie moduły, które zostaną załadowane i zaimportowane do bieżącej przestrzeni nazw, gdy użyjemy from package import *
.
Duża różnica polega na tym, że gdy pomijasz deklarację __all__
w pakiecie __init__.py
, deklaracja from package import *
nie zaimportuje niczego w ogóle(z wyjątkami opisanymi w dokumentacji, patrz link powyżej).
Z drugiej strony, jeśli pominiesz __all__
w module, "import z gwiazdką" zaimportuje wszystkie nazwy (nie zaczynające się od podkreślenia) zdefiniowane w module.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-16 19:01:48
Wyjaśnij _ _ wszystko _ _ w Pythonie?
Ciągle widzę zmienną
__all__
ustawioną w różnych plikach__init__.py
.Co to robi?
Co robi __all__
?
Deklaruje semantycznie "publiczne" nazwy z modułu. Jeśli w __all__
istnieje nazwa, oczekuje się, że użytkownicy będą jej używać i mogą mieć nadzieję, że nie ulegnie ona zmianie.
Będzie też miał programowy dotyczy:
import *
__all__
w module, np. module.py
:
__all__ = ['foo', 'Bar']
Oznacza, że kiedy import *
z modułu, importowane są tylko te nazwy w __all__
:
from module import * # imports foo and Bar
Narzędzia dokumentacji
Narzędzia do automatycznego uzupełniania dokumentacji i kodu mogą (w rzeczywistości powinny) również sprawdzać __all__
, aby określić, jakie nazwy mają być wyświetlane jako dostępne z modułu.
__init__.py
sprawia, że katalog jest pakietem Pythona
Z docs :
Pliki
__init__.py
są wymagane, aby Python traktował katalogi jako zawierające Pakiety; ma to na celu uniemożliwienie katalogom o wspólnej nazwie, takim jak string, niezamierzonego ukrywania poprawnych modułów, które pojawią się później na ścieżce wyszukiwania modułów.W najprostszym przypadku
__init__.py
może być tylko pustym plikiem, ale może również wykonać kod inicjalizacyjny dla pakietu lub ustawić zmienną__all__
.
Więc __init__.py
może zadeklarować __all__
dla pakiet.
Zarządzanie API:
Pakiet zazwyczaj składa się z modułów, które mogą importować się nawzajem, ale które muszą być powiązane razem z plikiem __init__.py
. Ten plik sprawia, że katalog jest prawdziwym pakietem Pythona. Na przykład, powiedzmy, że masz następujące:
package/
|-__init__.py # makes directory a Python package
|-module_1.py
|-module_2.py
W __init__.py
piszesz:
from module_1 import *
from module_2 import *
I w module_1
masz:
__all__ = ['foo',]
I w module_2
masz:
__all__ = ['Bar',]
A teraz zaprezentowałeś kompletne api że ktoś inny może użyć podczas importowania pakietu, w ten sposób:
import package
package.foo()
package.Bar()
I nie będą miały wszystkich innych nazw, których użyłeś podczas tworzenia modułów, zaśmiecających przestrzeń nazw package
.
__all__
w __init__.py
Po więcej pracy, być może zdecydowałeś, że moduły są zbyt duże i trzeba je podzielić. Więc wykonujesz następujące czynności:
package/
|-__init__.py
|-module_1/
| |-__init__.py
| |-foo_implementation.py
|-module_2/
|-__init__.py
|-Bar_implementation.py
I w każdym __init__.py
deklarujesz __all__
, np. w module_1:
from foo_implementation import *
__all__ = ['foo']
Oraz moduł_2 __init__.py
:
from Bar_implementation import *
__all__ = ['Bar']
I możesz łatwo dodać do API rzeczy, którymi możesz zarządzać na poziomie podpakietu zamiast poziomu modułu podpakietu. Jeśli chcesz dodać nową nazwę do API, po prostu zaktualizuj __init__.py
, np. w module_2:
from Bar_implementation import *
from Baz_implementation import *
__all__ = ['Bar', 'Baz']
A jeśli nie jesteś gotowy do opublikowania Baz
W API najwyższego poziomu, na twoim najwyższym poziomie __init__.py
możesz mieć:
from module_1 import * # also constrained by __all__'s
from module_2 import * # in the __init__.py's
__all__ = ['foo', 'Bar'] # further constraining the names advertised
Jeśli użytkownik jest świadomy dostępności Baz
, może z niej korzystać:
import package
package.Baz()
Ale jeśli nie wiedzą o tym, Inne narzędzia (jak pydoc) nie poinformują ich.
Możesz to później zmienić, gdyBaz
będzie gotowy na prime time:
from module_1 import *
from module_2 import *
__all__ = ['foo', 'Bar', 'Baz']
Prefiks _
kontra __all__
:
Domyślnie Python eksportuje wszystkie nazwy, które nie zaczynają się od _
. Z pewnością mógłbyś polegać na tym mechanizmie. Niektóre pakiety w standardowej bibliotece Pythona, w rzeczywistości polegają na tym, ale aby to zrobić, alias ich importu, na przykład, w ctypes/__init__.py
:
import os as _os, sys as _sys
Użycie konwencji _
może być bardziej eleganckie, ponieważ usuwa nadmiarowość nazywania nazw ponownie. Ale dodaje redundancję dla importu (jeśli masz ich dużo) i jest {105]} łatwo zapomnieć o konsekwentnym robieniu tego-i ostatnią rzeczą, którą chcesz, to mieć na czas nieokreślony wsparcie czegoś, co zamierzałeś tylko być szczegółem implementacji, tylko dlatego, że zapomniałeś przedrostka _
podczas nazywania funkcji.
I osobiście napisz __all__
na 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 bibliotece standardowej również używa __all__
.
Kiedy Unikanie __all__
ma sens
Sensowne jest trzymanie się _
konwencji przedrostków zamiast __all__
, gdy:
- wciąż znajdujesz się w trybie wczesnego rozwoju, nie masz użytkowników i ciągle poprawiasz swoje API.
- Maybe you masz użytkowników, ale masz jednostki, które obejmują API, i nadal aktywnie dodajesz do API i poprawiasz w rozwoju.
An export
dekorator
Minusem używania __all__
jest to, że musisz napisać nazwy funkcji i klas eksportowanych dwa razy - a informacje są oddzielone od definicji. Możemy użyć dekoratora do rozwiązania tego problemu.
Mam pomysł na taki dekorator eksportu z David Beazley ' s dyskusja na temat opakowań. To wdrożenie wydaje się działać dobrze u tradycyjnego importera CPython. Jeśli masz specjalny haczyk lub system importu, nie gwarantuję tego, ale jeśli go przyjmiesz, wycofanie się jest dość trywialne - będziesz musiał ręcznie dodać nazwy z powrotem do __all__
import sys
def export(fn):
mod = sys.modules[fn.__module__]
if hasattr(mod, '__all__'):
mod.__all__.append(fn.__name__)
else:
mod.__all__ = [fn.__name__]
return fn
I wtedy, gdzie zdefiniujesz __all__
, robisz to:
$ cat > main.py
from lib import export
__all__ = [] # optional - we create a list if __all__ is not there.
@export
def foo(): pass
@export
def bar():
'bar'
def main():
print('main')
if __name__ == '__main__':
main()
I to działa dobrze, czy uruchomić jako główne lub importowane przez inną funkcję.
$ cat > run.py
import main
main.main()
$ python run.py
main
W tym celu należy wykonać następujące czynności:]}
$ cat > run.py
from main import *
foo()
bar()
main() # expected to error here, not exported
$ python run.py
Traceback (most recent call last):
File "run.py", line 4, in <module>
main() # expected to error here, not exported
NameError: name 'main' is not defined
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-27 13:06:31
Zmienia również to, co pydoc pokaże:
Module1.py
a = "A"
b = "B"
c = "C"
Module2.py
__all__ = ['a', 'b']
a = "A"
b = "B"
c = "C"
$ pydoc module1
Help on module module1: NAME module1 FILE module1.py DATA a = 'A' b = 'B' c = 'C'
$ pydoc module2
Help on module module2: NAME module2 FILE module2.py DATA __all__ = ['a', 'b'] a = 'A' b = 'B'
Deklaruję __all__
we wszystkich moich modułach, a także podkreślam wewnętrzne szczegóły, naprawdę pomagają one w korzystaniu z rzeczy, których nigdy wcześniej nie używałeś w sesjach Live interpreter.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-15 03:22:29