Quelqu'un peut-il expliquer __all__ en Python?

983

J'utilise de plus en plus Python et je vois toujours la variable __all__définie dans différents __init__.pyfichiers. Quelqu'un peut-il expliquer ce que cela fait?

varikin
la source

Réponses:

566

Il s'agit d'une liste d'objets publics de ce module, telle qu'interprétée par import *. Il remplace la valeur par défaut de masquer tout ce qui commence par un trait de soulignement.

Jimmy
la source
146
Les objets qui commencent par un trait de soulignement, ou qui ne sont pas mentionnés dans __all__si __all__est présent, ne sont pas exactement cachés; ils peuvent être vus et consultés parfaitement normalement si vous connaissez leurs noms. Ce n'est que dans le cas d'une "importation", ce qui n'est pas recommandé de toute façon, que la distinction a un poids.
Brandon Rhodes
28
@BrandonRhodes: ce n'est pas tout à fait vrai non plus: il est recommandé d'importer uniquement les modules pour lesquels vous savez être conçus import *(comme par exemple tk). Un bon indice si tel est le cas est la présence de __all__ou de noms commençant par un soulignement dans le code du module.
moutons volants
12
Interfaces publiques et internes - python.org/dev/peps/pep-0008/#id50 , Pour mieux prendre en charge l'introspection, les modules doivent déclarer explicitement les noms dans leur API publique en utilisant l'attribut __all__. La définition de __all__ sur une liste vide indique que le module n'a pas d'API publique.
débogage le
Je ne suis pas sûr que si elles tkétaient publiées aujourd'hui (ou même en 2012), la pratique recommandée serait d'utiliser from tk import *. Je pense que la pratique est acceptée en raison de l'inertie et non de la conception intentionnelle.
chepner
Comme BrandonRhodes le fait remarquer, ce n'est vraiment pas correct
duhaime
947

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

Par exemple, le code suivant dans un foo.pyexporte explicitement les symboles baret 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 ce qui __all__précède est mis en commentaire, ce code s'exécutera ensuite jusqu'à la fin, car 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/tutorial/modules.html#importing-from-a-package

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

Alec Thomas
la source
1
ne devrions-nous pas imprimer baz comme print(baz())?
John Cole
@JohnCole baz est un objet fonction et baz () exécutera l'objet fonction
Bhanu Tez
@BhanuTez exactement. print(baz)Imprime donc quelque chose comme <function baz at 0x7f32bc363c10>alors print(baz())imprimebaz
John Cole
225

Expliquez __all__ en Python?

Je vois toujours la variable __all__définie dans différents __init__.pyfichiers.

Qu'est-ce que cela fait?

Que fait __all__-il?

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

Il aura également des effets programmatiques:

import *

__all__dans un module, par exemple module.py:

__all__ = ['foo', 'Bar']

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

from module import *               # imports foo and Bar

Outils de documentation

Les outils de documentation et d'auto-complétion de code peuvent (en fait, devraient) également inspecter le __all__pour déterminer les noms à afficher comme disponibles à partir d'un module.

__init__.py fait d'un répertoire un package Python

De la documentation :

Les __init__.pyfichiers sont nécessaires pour que Python traite les répertoires comme contenant des packages; cela est fait pour empêcher les répertoires avec un nom commun, tel que chaîne, de masquer involontairement des modules valides qui se produisent plus tard sur le chemin de recherche de module.

Dans le cas le plus simple, il __init__.pypeut simplement s'agir d'un fichier vide, mais il peut également exécuter le code d'initialisation du package ou définir la __all__variable.

Ainsi, le __init__.pypeut déclarer le __all__pour un package .

Gérer une API:

Un package est généralement composé de modules qui peuvent s’importer les uns les autres, mais qui sont nécessairement liés à un __init__.pyfichier. Ce fichier est ce qui fait du répertoire un véritable package Python. Par exemple, supposons que vous ayez les fichiers suivants dans un package:

package
├── __init__.py
├── module_1.py
└── module_2.py

Créons ces fichiers avec Python afin de pouvoir suivre - vous pouvez coller ce qui suit dans un shell Python 3:

from pathlib import Path

package = Path('package')
package.mkdir()

(package / '__init__.py').write_text("""
from .module_1 import *
from .module_2 import *
""")

package_module_1 = package / 'module_1.py'
package_module_1.write_text("""
__all__ = ['foo']
imp_detail1 = imp_detail2 = imp_detail3 = None
def foo(): pass
""")

package_module_2 = package / 'module_2.py'
package_module_2.write_text("""
__all__ = ['Bar']
imp_detail1 = imp_detail2 = imp_detail3 = None
class Bar: pass
""")

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

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

Et le package n'aura pas tous les autres détails d'implémentation que vous avez utilisés lors de la création de vos modules encombrant l' packageespace de noms.

__all__ dans __init__.py

Après plus de travail, vous avez peut-être décidé que les modules sont trop gros (comme plusieurs milliers de lignes?) Et doivent être divisés. Vous procédez donc comme suit:

package
├── __init__.py
├── module_1
│   ├── foo_implementation.py
│   └── __init__.py
└── module_2
    ├── Bar_implementation.py
    └── __init__.py

Créez d'abord les sous-répertoires avec les mêmes noms que les modules:

subpackage_1 = package / 'module_1'
subpackage_1.mkdir()
subpackage_2 = package / 'module_2'
subpackage_2.mkdir()

Déplacez les implémentations:

package_module_1.rename(subpackage_1 / 'foo_implementation.py')
package_module_2.rename(subpackage_2 / 'Bar_implementation.py')

créez des __init__.pys pour les sous-packages qui déclarent le __all__pour chacun:

(subpackage_1 / '__init__.py').write_text("""
from .foo_implementation import *
__all__ = ['foo']
""")
(subpackage_2 / '__init__.py').write_text("""
from .Bar_implementation import *
__all__ = ['Bar']
""")

Et maintenant, vous avez toujours l'api provisionné au niveau du package:

>>> import package
>>> package.foo()
>>> package.Bar()
<package.module_2.Bar_implementation.Bar object at 0x7f0c2349d210>

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

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

Et si vous n'êtes pas prêt à publier Bazdans l'API de niveau supérieur, dans votre niveau supérieur, __init__.pyvous pourriez avoir:

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 informeront pas.

Vous pouvez plus tard changer cela quand Bazest prêt pour les heures de grande écoute:

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 un _. Vous avez certainement pu compter sur ce mécanisme. Certains paquets dans la bibliothèque standard de Python, en fait, ne comptent sur ce point , mais de le faire, ils alias leurs importations, par exemple, en ctypes/__init__.py:

import os as _os, sys as _sys

L'utilisation de la _convention peut être plus élégante car elle supprime la redondance de l'attribution de nouveaux noms. Mais cela ajoute la redondance des importations (si vous en avez beaucoup) et il est facile d'oublier de le faire de manière cohérente - et la dernière chose que vous voulez est de devoir prendre en charge indéfiniment quelque chose que vous vouliez être seulement un détail d'implémentation, juste parce que vous avez oublié de préfixer un _lorsque vous nommez une fonction.

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

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

Quand éviter a du __all__sens

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

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

Un exportdécorateur

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

J'ai eu l'idée d'un tel décorateur d'exportation à partir de l'exposé de David Beazley sur l'emballage. Cette implémentation semble bien fonctionner dans l'importateur traditionnel de CPython. Si vous avez un système ou un crochet d'importation spécial, je ne le garantis pas, mais si vous l'adoptez, il est assez banal de le retirer - vous aurez juste besoin d'ajouter manuellement les noms dans le__all__

Ainsi, par exemple, dans une bibliothèque d'utilitaires, vous définiriez 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

puis, où vous définiriez un __all__, vous faites ceci:

$ 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 *fonctionnera également:

$ 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
Aaron Hall
la source
1
Renvoi: J'ai mentionné votre décorateur dans cette réponse CW à la question de savoir comment écrire un @exportdécorateur.
MvG
13
Cela a été à lui seul la réponse la plus utile que j'ai vue en ce qui concerne l'aide à un développeur de python relativement nouveau pour comprendre le processus d'importation de modules / packages avec __init__.pyet l'utilisation de__all__
Brett Reinhard
Cela m'aide beaucoup. Mon problème, cependant, est que les sous-modules que je veux importer sont tous des fichiers générés avec beaucoup de symboles dans leurs symboles que j'aimerais supprimer, sans avoir à vérifier manuellement que __all__c'est correct.
Mike C
@MikeC alors vous devriez peut-être aussi générer votre __all__- mais alors je dirais que vous avez une API instable ... Ce serait quelque chose pour avoir des tests d'acceptation complets.
Aaron Hall
@AaronHall "ils n'auront pas tous les autres noms ... encombrant l'espace de noms du package" Mais ils auront les noms module_1et module_2; est-il OK d'inclure un explicite del module_1dans __init__.py? Ai-je tort de penser que cela en vaut la peine?
Mike C
176

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

Toutes les autres réponses se réfèrent aux modules . La question d'origine est explicitement mentionnée __all__dans les __init__.pyfichiers, il s'agit donc de packages python .

Généralement, __all__n'entre en jeu que lorsque la from xxx import *variante de l' importinstruction 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 packages est décrit ici en détail.

En bref, __all__au niveau du package fait à peu près la même chose que pour les modules, sauf qu'il traite des modules dans le package (contrairement à la spécification de noms dans le module ). Spécifie donc __all__tous les modules qui doivent être chargés et importés dans l'espace de noms courant lorsque nous les utilisons from package import *.

La grande différence est que lorsque vous omettez la déclaration de __all__dans un package __init__.py, l'instruction from package import *n'importera rien du tout (avec des 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.

MartinStettner
la source
29
from package import *importera toujours tout ce qui est défini dans __init__.py, même s'il n'y en a pas all. La différence importante est que sans __all__cela, il n'importera pas automatiquement les modules définis dans le répertoire du package.
Nikratio
Lorsque tout contient [foo, bar] et dans le fichier test.py si nous utilisons: depuis le package import *, alors, foo et bar sont-ils importés dans l'espace de noms local de test.py ou dans le propre espace de noms foo et bars?
variable
87

Cela change également ce que pydoc affichera:

module1.py

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

module2.py

__all__ = ['a', 'b']

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

$ pydoc module1

Aide sur le module module1:

NOM
    module 1

FICHIER
    module1.py

DONNÉES 
    a = 'A'
     b = 'B'
     c = 'C'

$ pydoc module2

Aide sur le module module2:

NOM
    module2

FICHIER
    module2.py

DONNÉES 
    __all__ = ['a', 'b']
     a = 'A'
     b = 'B'

Je déclare __all__dans tous mes modules, ainsi que les détails internes soulignés, que cela aide vraiment lorsque vous utilisez des choses que vous n'avez jamais utilisées auparavant dans des sessions d'interprétariat en direct.

L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
la source
55

__all__personnalise *enfrom <module> import *

__all__personnalise *enfrom <package> import *


Un module est un .pyfichier destiné à être importé.

Un package est un répertoire avec un __init__.pyfichier. Un package contient généralement des modules.


MODULES

""" cheese.py - an example module """

__all__ = ['swiss', 'cheddar']

swiss = 4.99
cheddar = 3.99
gouda = 10.99

__all__permet aux humains de connaître les fonctionnalités "publiques" d'un module . [ @AaronHall ] De plus, pydoc les reconnaît. [ @Longpoke ]

depuis l' importation de module *

Voyez comment swisset cheddarsont 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 __all__, aucun symbole (qui ne commence pas par un trait de soulignement) aurait été disponible.


Les importations sans *sont pas affectées par__all__


module d' importation

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

à partir des noms d' importation de module

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

module d' importation en tant que nom local

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

PAQUETS

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

Voici un extrait du connecteur Python MySQL __init__.py :

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

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

    # Error handling
    'Error', 'Warning',

    ...etc...

    ]

Le cas par défaut, astérisque sans no __all__pour un package , est compliqué, car le comportement évident serait coûteux: utiliser le système de fichiers pour rechercher tous les modules du package. Au lieu de cela, dans ma lecture des documents, seuls les objets définis dans __init__.pysont importés:

Si __all__non défini, l'instruction from sound.effects import *ne pas importer toutes les sous - modules du package sound.effectsdans l'espace de noms en cours; il garantit uniquement que le package sound.effectsa été importé (éventuellement en exécutant n'importe quel code d'initialisation __init__.py), puis importe les noms définis dans le package. Cela inclut tous les noms définis (et les sous-modules explicitement chargés) par __init__.py. Il inclut également tous les sous-modules du package qui ont été explicitement chargés par les instructions d'importation précédentes.


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

[ PEP 8 , @ToolmakerSteve]

Bob Stein
la source
2
Je aime vraiment cette réponse, mais je me manque les informations sur ce qui est le comportement par défaut from <package> import *sans __all__en __init__.pyqui n'importer l' un des modules .
radzak
Merci @Jatimir, j'ai clarifié du mieux que je pouvais sans lancer d'expériences. Je voulais presque dire que ce cas (astérisque sans tout pour un paquet) se comporte comme s'il __init__.pys'agissait d'un module . Mais je ne suis pas sûr que ce soit exact, ou en particulier si les objets avec préfixe de soulignement sont exclus. De plus, j'ai séparé plus clairement les sections sur les MODULES et les FORFAITS. Tes pensées?
Bob Stein
49

Du (Un Unofficial) 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 dont les noms sont définis ou importés par ce module. Les noms indiqués __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__doit contenir l'intégralité de l'API publique. Il est destiné à éviter d'exporter accidentellement des é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).

Lasse V. Karlsen
la source
Le lien répertorié est mort. mais texte trouvé textuellement sur vdocuments.net/… & ici: dokumen.tips/documents/reference-567bab8d6118a.html
JayRizzo
8

__all__est utilisé pour documenter l'API publique d'un module Python. Bien qu'il soit facultatif, __all__doit ê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 dont les noms sont définis ou importés par ce module. Les noms indiqués __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__doit contenir l'intégralité de l'API publique. Il est destiné à éviter d'exporter accidentellement des é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 une formulation similaire, bien qu'il indique également clairement que les noms importés ne font pas partie de l'API publique en cas d' __all__absence:

Pour mieux prendre en charge l'introspection, les modules doivent déclarer explicitement les noms dans leur API publique à l'aide de l' __all__attribut. La définition __all__d'une liste vide indique que le module n'a pas d'API publique.

[...]

Les noms importés doivent toujours être considérés comme un détail d'implémentation. Les autres modules ne doivent pas s'appuyer sur un accès indirect à ces noms importés, sauf s'ils font partie explicitement documentée de l'API du module conteneur, tel que os.pathle __init__module d' un package qui expose les fonctionnalités des sous-modules.

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

L'instruction import utilise la convention suivante: si le __init__.pycode d' un package définit une liste nommée __all__, elle est considérée comme la liste des noms de module qui doivent être importés lorsqu'il from package import *est rencontré.

Mihai Capotă
la source
8

Réponse courte

__all__affecte les 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__, alors from foo import *importera uniquement les noms définis dans foo/__init__.py.

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

  • (Explicite) Si nous définissons __all__ = [ <name1>, ... ], alors from foo import *importera uniquement 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 à l'aide de __all__.

Vous pouvez voir le document Python ici .

Cyker
la source
6

__all__affecte la façon dont from foo import *fonctionne.

Le code qui se trouve à l'intérieur d'un corps de module (mais pas dans le corps d'une fonction ou d'une classe) peut utiliser un astérisque ( *) dans une frominstruction:

from foo import *

Les *demandes que tous les attributs du module foo(à l'exception de ceux commençant par des traits de soulignement) soient liés en tant que variables globales dans le module d'importation. Lorsqu'il fooa un attribut __all__, la valeur de l'attribut est la liste des noms liés par ce type de fromdéclaration.

Si fooest un package et qu'il __init__.pydéfinit une liste nommée __all__, il est considéré comme la liste des noms de sous-modules qui doivent être importés lorsqu'il from foo import *est rencontré. Si __all__n'est pas défini, l'instruction from foo import *importe les noms définis dans le package. Cela inclut tous les noms définis (et les sous-modules explicitement chargés) par __init__.py.

Notez que __all__cela ne doit pas nécessairement être une liste. Selon la documentation de l' importinstruction , si elle est définie, elle __all__doit être une séquence de chaînes qui sont des noms définis ou importés par le module. Vous pouvez donc aussi bien utiliser un tuple pour économiser de la mémoire et des cycles CPU. N'oubliez pas une virgule au cas où le module définirait un seul nom public:

__all__ = ('some_name',)

Voir aussi Pourquoi «importer *» est-il mauvais?

Eugene Yarmash
la source
1

Ceci est défini dans PEP8 ici :

Noms de variables globales

(Espérons que ces variables sont destinées à être utilisées dans un seul module.) Les conventions sont à peu près les mêmes que celles des fonctions.

Les modules conçus pour être utilisés via from M import *doivent utiliser le __all__mécanisme pour empêcher l'exportation de globaux, ou utiliser l'ancienne convention consistant à préfixer ces globaux avec un trait de soulignement (ce que vous voudrez peut-être faire pour indiquer que ces globaux sont "module non public").

PEP8 fournit des conventions de codage pour le code Python comprenant la bibliothèque standard dans la distribution Python principale. Plus vous suivez cela, plus vous êtes proche de l'intention d'origine.

prosti
la source