Question Quelqu'un peut-il expliquer __all__ en Python?


J'utilise Python de plus en plus, et je continue à voir la variable __all__ définir dans différents __init__.py des dossiers. Quelqu'un peut-il expliquer ce que cela fait?


630
2017-09-04 21:28


origine


Réponses:


C'est une liste d'objets publics de ce module, telle qu'interprétée par import *. Il remplace le défaut de cacher tout ce qui commence par un trait de soulignement.


318
2017-09-04 21:30



Lié à, mais pas explicitement mentionné ici, est exactement quand __all__ est utilisé. C'est une liste de chaînes définissant quels symboles d'un module seront exportés from <module> import * est utilisé sur le module.

Par exemple, le code suivant dans un foo.py exporte explicitement les symboles bar et baz:

__all__ = ['bar', 'baz']

waz = 5
bar = 10
def baz(): return 'baz'

Ces symboles peuvent ensuite être importés comme suit:

from foo import *

print bar
print baz

# The following will trigger an exception, as "waz" is not exported by the module
print waz

Si la __all__ ci-dessus est commenté, ce code sera ensuite exécuté à la fin, comme le comportement par défaut de import * est d'importer tous les symboles qui ne commencent pas par un trait de soulignement, à partir de l'espace de noms donné.

Référence: https://docs.python.org/3.5/tutorial/modules.html#importing-from-a-package

REMARQUE:  __all__ affecte le from <module> import * comportement seulement. Les membres qui ne sont pas mentionnés dans __all__ sont toujours accessibles depuis l'extérieur du module et peuvent être importés avec from <module> import <member>.


676
2017-09-15 15:49



J'ajoute juste ceci pour être précis:

Toutes les autres réponses se réfèrent à modules. La question originale explicitement mentionnée __all__ dans __init__.py fichiers, il s'agit donc de python paquets.

Généralement, __all__ ne vient en jeu lorsque le from xxx import * variante du import déclaration est utilisée. Cela s'applique aux packages ainsi qu'aux modules.

Le comportement des modules est expliqué dans les autres réponses. Le comportement exact des paquets est décrit ici en détail.

En bref, __all__ au niveau de l'emballage fait à peu près la même chose que pour les modules, sauf qu'il s'agit de modules dans le paquet  (contrairement à la spécification noms dans le module). Alors __all__ spécifie tous les modules qui doivent être chargés et importés dans l'espace de noms actuel lorsque nous utilisons from package import *.

La grande différence est que lorsque vous omettre la déclaration de __all__ dans un paquet __init__.py, la déclaration from package import * n'importera rien du tout (avec les exceptions expliquées dans la documentation, voir le lien ci-dessus).

D'un autre côté, si vous omettez __all__ dans un module, l '"importation étoilée" importera tous les noms (ne commençant pas par un trait de soulignement) définis dans le module.


134
2018-05-16 19:01



Expliquer __all__ en Python?

Je continue à voir la variable __all__ définir dans différents __init__.py des dossiers.

Qu'est-ce que ça fait?

Qu'est-ce que __all__ faire?

Il déclare les noms sémantiquement "publics" d'un module. S'il y a un nom dans __all__, les utilisateurs sont censés l'utiliser et ils peuvent s'attendre à ce que cela ne change pas.

Il aura aussi des effets programmatiques:

import *

__all__ dans un module, par ex. module.py:

__all__ = ['foo', 'Bar']

signifie que lorsque vous import * à partir du module, seuls les noms dans le __all__sont importés:

from module import *               # imports foo and Bar

Outils de documentation

La documentation et les outils d'autocomplétion de code peuvent (en fait, devraient) inspecter __all__ pour déterminer quels noms montrer comme disponibles à partir d'un module.

__init__.py fait un répertoire un paquet Python

Du docs:

le __init__.py les fichiers sont requis pour que Python traite les répertoires comme contenant des paquets; Ceci est fait pour empêcher les répertoires avec un nom commun, comme une chaîne, de masquer involontairement des modules valides qui apparaissent plus tard dans le chemin de recherche du module.

Dans le cas le plus simple, __init__.py peut juste être un fichier vide, mais il peut également exécuter le code d'initialisation pour le paquet ou définir le __all__ variable.

Alors le __init__.py peut déclarer le __all__ pour un paquet.

Gérer une API:

Un paquet est typiquement constitué de modules qui peuvent s'importer, mais qui sont nécessairement liés avec un __init__.py fichier. Ce fichier est ce qui fait du répertoire un vrai paquet Python. Par exemple, disons que vous avez:

 package/
   |-__init__.py # makes directory a Python package
   |-module_1.py
   |-module_2.py

dans le __init__.py vous écrivez:

from module_1 import *
from module_2 import *

et en module_1 tu as:

__all__ = ['foo',]

et en module_2 tu as:

__all__ = ['Bar',]

Et maintenant vous avez présenté une API complète que quelqu'un d'autre peut utiliser lors de l'importation de votre paquet, comme ceci:

import package
package.foo()
package.Bar()

Et ils n'auront pas tous les autres noms que vous avez utilisés lors de la création de vos modules encombrant le package espace de nommage.

__all__ dans __init__.py

Après plus de travail, vous avez peut-être décidé que les modules sont trop grands et doivent être séparés. Donc, vous faites ce qui suit:

 package/
   |-__init__.py
   |-module_1/
   |  |-__init__.py
   |  |-foo_implementation.py
   |-module_2/
      |-__init__.py
      |-Bar_implementation.py

Et dans chaque __init__.py vous déclarez un __all__, par exemple. dans module_1:

from foo_implementation import *
__all__ = ['foo']

Et le module_2 __init__.py:

from Bar_implementation import *
__all__ = ['Bar']

Et vous pouvez facilement ajouter des choses à votre API que vous pouvez gérer au niveau du sous-paquetage au lieu du niveau de module du sous-paquetage. Si vous souhaitez ajouter un nouveau nom à l'API, vous devez simplement mettre à jour __init__.py, par exemple. dans le module_2:

from Bar_implementation import *
from Baz_implementation import *
__all__ = ['Bar', 'Baz']

Et si vous n'êtes pas prêt à publier Baz dans l'API de haut niveau, dans votre niveau supérieur __init__.py tu aurais pu:

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

et si vos utilisateurs sont conscients de la disponibilité de Baz, ils peuvent l'utiliser:

import package
package.Baz()

mais s'ils ne le savent pas, d'autres outils (comme pydoc) ne les informera pas.

Vous pouvez plus tard changer cela quand Baz est prêt pour le prime time:

from module_1 import *
from module_2 import *
__all__ = ['foo', 'Bar', 'Baz']

Préfixe _ contre __all__:

Par défaut, Python exportera tous les noms qui ne commencent pas par _. Vous avez certainement pourrait compter sur ce mécanisme. Certains paquets dans la bibliothèque standard Python, en fait, faire compter sur cela, mais pour ce faire, ils alias leurs importations, par exemple, dans ctypes/__init__.py:

import os as _os, sys as _sys

En utilisant le _ La convention peut être plus élégante parce qu'elle supprime la redondance de nommer les noms à nouveau. Mais il ajoute la redondance pour les importations (si vous en avez beaucoup) et c'est facile d'oublier de le faire de manière cohérente - et la dernière chose que vous voulez est d'avoir à supporter indéfiniment quelque chose que vous vouliez seulement être un détail d'implémentation, juste parce que vous avez oublié de préfixer un _ lors de la désignation d'une fonction.

J'écris personnellement un __all__au début de mon cycle de vie de développement pour les modules afin que les autres utilisateurs de mon code sachent ce qu'ils doivent utiliser et ne pas utiliser.

La plupart des paquets de la bibliothèque standard utilisent également __all__.

En évitant __all__ logique

Il est logique de s'en tenir à la _ préfixe convention au lieu de __all__ quand:

  • Vous êtes toujours en mode de développement précoce et n'avez pas d'utilisateurs, et vous peaufinez constamment votre API.
  • Peut-être avez-vous des utilisateurs, mais vous avez des tests unitaires qui couvrent l'API, et vous continuez d'ajouter activement à l'API et de peaufiner le développement.

Un export décorateur

L'inconvénient de l'utilisation __all__ est que vous devez écrire les noms des fonctions et des classes exportées deux fois - et les informations sont conservées séparément des définitions. nous pourrait utiliser un décorateur pour résoudre ce problème.

J'ai eu l'idée d'un tel décorateur d'exportation à partir du discours de David Beazley sur l'emballage. Cette implémentation semble bien fonctionner dans l'importateur traditionnel de CPython. Si vous disposez d'un système d'importation ou d'un système d'importation spécial, je ne le garantis pas, mais si vous l'adoptez, il est relativement simple de revenir en arrière. Vous devrez simplement ajouter manuellement les noms dans le système. __all__

Ainsi, dans une bibliothèque d'utilitaires, par exemple, vous définissez le décorateur:

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

et puis, où vous définiriez un __all__, Tu fais cela:

$ 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()

Et cela fonctionne bien, qu'il soit exécuté en tant que principal ou importé par une autre fonction.

$ cat > run.py
import main
main.main()

$ python run.py
main

Et le provisionnement d'API avec import * travaillera aussi:

$ 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

105
2018-02-29 21:58



Cela change aussi ce que pydoc montrera:

module1.py

a = "A"
b = "B"
c = "C"

module2.py

__all__ = ['a', 'b']

a = "A"
b = "B"
c = "C"

$ pydoc module1

Aide sur module module1:

PRÉNOM
    module 1

FICHIER
    module1.py

LES DONNÉES
     une = 'A'
     b = 'B'
     c = 'C'

$ pydoc module2

Aide sur module module2:

PRÉNOM
    module2

FICHIER
    module2.py

LES DONNÉES
     __tout__ = ['a', 'b']
     une = 'A'
     b = 'B'

je déclare __all__ Dans tous mes modules, ainsi que de souligner les détails internes, ceux-ci aident vraiment lorsque vous utilisez des choses que vous n'avez jamais utilisées auparavant dans des sessions d'interprètes en direct.


83
2018-05-15 03:22



De (Un officieux) Wiki de référence Python:

Les noms publics définis par un module sont déterminés en vérifiant l'espace de noms du module pour une variable nommée __all__; s'il est défini, il doit s'agir d'une séquence de chaînes qui sont des noms définis ou importés par ce module. Les noms donnés dans __all__ sont tous considérés comme publics et doivent exister. Si __all__ n'est pas défini, l'ensemble des noms publics inclut tous les noms trouvés dans l'espace de noms du module qui ne commencent pas par un caractère de soulignement ("_"). __all__ devrait contenir toute l'API publique. Il est destiné à éviter l'exportation accidentelle d'éléments qui ne font pas partie de l'API (tels que les modules de bibliothèque qui ont été importés et utilisés dans le module).


48
2017-09-04 21:31



__all__ personnalise l'astérisque dans from <module> import *

__all__ personnalise l'astérisque dans from <package> import *


UNE module est un .py fichier destiné à être importé.

UNE paquetest un annuaire avec un __init__.py fichier. Un paquet contient généralement des modules.

""" cheese.py """

__all__ = ['swiss', 'cheddar']

swiss = 4.99
cheddar = 3.99
gouda = 10.99

__all__ permet aux humains de connaître les caractéristiques «publiques» d'un module.[@AaronHall]  En outre, pydoc les reconnaît.[@Longpoke]

de module importation *

Regarde comment swiss et cheddar sont introduits dans l'espace de noms local, mais pas gouda:

>>> from cheese import *
>>> swiss, cheddar
(4.99, 3.99)
>>> gouda
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'gouda' is not defined

Sans pour autant __all__, tout symbole (qui ne commence pas par un trait de soulignement) aurait été disponible.


Importations sans * ne sont pas affectés par __all__


importer module

>>> import cheese
>>> cheese.swiss, cheese.cheddar, cheese.gouda
(4.99, 3.99, 10.99)

de module importer des noms

>>> from cheese import swiss, cheddar, gouda
>>> swiss, cheddar, gouda
(4.99, 3.99, 10.99)

importer module comme nom local

>>> import cheese as ch
>>> ch.swiss, ch.cheddar, ch.gouda
(4.99, 3.99, 10.99)

dans le __init__.py fichier d'un paquet  __all__ est une liste de chaînes avec les noms de modules publics ou d'autres objets. Ces fonctionnalités sont disponibles pour les importations génériques. Comme avec les modules, __all__ personnalise le * lors de l'importation de caractères génériques à partir du package.[@MartinStettner] 

Voici un extrait de la Connecteur MySQL Python  __init__.py:

__all__ = [
    'MySQLConnection', 'Connect', 'custom_error_exception',

    # Some useful constants
    'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
    'HAVE_CEXT',

    # Error handling
    'Error', 'Warning',

    ...etc...

    ]

Les importations de caractères génériques ... devraient être évitées car elles [confondent] les lecteurs et de nombreux outils automatisés.

[PEP 8, @ToolmakerSteve]


19
2018-03-20 20:20



__all__ est utilisé pour documenter l'API publique d'un module Python. Bien que ce soit optionnel, __all__ Devrait être utilisé.

Voici l'extrait pertinent de la référence du langage Python:

Les noms publics définis par un module sont déterminés en vérifiant l'espace de noms du module pour une variable nommée __all__; s'il est défini, il doit s'agir d'une séquence de chaînes qui sont des noms définis ou importés par ce module. Les noms donnés dans __all__ sont tous considérés comme publics et doivent exister. Si __all__ n'est pas défini, l'ensemble des noms publics inclut tous les noms trouvés dans l'espace de noms du module qui ne commencent pas par un caractère de soulignement ('_'). __all__ devrait contenir toute l'API publique. Il est destiné à éviter l'exportation accidentelle d'éléments qui ne font pas partie de l'API (tels que les modules de bibliothèque qui ont été importés et utilisés dans le module).

PEP 8 utilise un libellé similaire, bien qu'il précise également que les noms importés ne font pas partie de l'API publique lorsque __all__ est absent:

Pour mieux prendre en charge l'introspection, les modules doivent déclarer explicitement les noms dans leur API publique à l'aide du __all__ attribut. Réglage __all__ à une liste vide indique que le module n'a pas d'API publique.

[...]

Les noms importés doivent toujours être considérés comme des détails d'implémentation. Les autres modules ne doivent pas s'appuyer sur un accès indirect à de tels noms importés, sauf s'il s'agit d'une partie explicitement documentée de l'API du module conteneur, telle que os.path ou un paquet de __init__ module qui expose la fonctionnalité des sous-modules.

En outre, comme indiqué dans d'autres réponses, __all__ est utilisé pour activer importation de caractères génériques pour les packages:

La déclaration d'importation utilise la convention suivante: si un paquet est __init__.py le code définit une liste nommée __all__, il est pris pour être la liste des noms de modules qui doivent être importés quand from package import * est rencontré.


7
2018-04-26 01:39



Réponse courte

__all__ affecte from <module> import * déclarations.

Longue réponse

Considérez cet exemple:

foo
├── bar.py
└── __init__.py

Dans foo/__init__.py:

  • (Implicite) Si nous ne définissons pas __all__, puis from foo import * importera uniquement les noms définis dans foo/__init__.py.

  • (Explicite) Si nous définissons __all__ = [], puis from foo import * n'importera rien.

  • (Explicite) Si nous définissons __all__ = [ <name1>, ... ], puis from foo import * importera seulement ces noms.

Notez que dans le cas implicite, python n'importera pas de noms commençant par _. Cependant, vous pouvez forcer l'importation de ces noms en utilisant __all__.

Vous pouvez voir le document Python ici.


2
2018-04-03 01:19