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?

Author: varikin, 0000-00-00

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.

 334
Author: Jimmy,
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>.

 696
Author: Alec Thomas,
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.

 138
Author: MartinStettner,
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ć, gdy Baz 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__

Na przykład w bibliotece narzędzi można zdefiniować dekorator:]}
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
 110
Author: Aaron Hall,
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.

 83
Author: L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳,
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