J'utilise de plus en plus Python et je vois toujours la variable __all__
définie dans différents __init__.py
fichiers. Quelqu'un peut-il expliquer ce que cela fait?
python
syntax
namespaces
varikin
la source
la source
__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.import *
(comme par exempletk
). 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.tk
étaient publiées aujourd'hui (ou même en 2012), la pratique recommandée serait d'utiliserfrom tk import *
. Je pense que la pratique est acceptée en raison de l'inertie et non de la conception intentionnelle.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'ilfrom <module> import *
est utilisé sur le module.Par exemple, le code suivant dans un
foo.py
exporte explicitement les symbolesbar
etbaz
:Ces symboles peuvent ensuite être importés comme suit:
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 deimport *
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__
affectefrom <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 avecfrom <module> import <member>
.la source
print(baz())
?print(baz)
Imprime donc quelque chose comme<function baz at 0x7f32bc363c10>
alorsprint(baz())
imprimebaz
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 exemplemodule.py
:signifie que lorsque vous
import *
depuis le module, seuls les noms dans le__all__
sont importés: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 PythonDe la documentation :
Ainsi, le
__init__.py
peut 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__.py
fichier. 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:Créons ces fichiers avec Python afin de pouvoir suivre - vous pouvez coller ce qui suit dans un shell Python 3:
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:
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'
package
espace 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:
Créez d'abord les sous-répertoires avec les mêmes noms que les modules:
Déplacez les implémentations:
créez des
__init__.py
s pour les sous-packages qui déclarent le__all__
pour chacun:Et maintenant, vous avez toujours l'api provisionné au niveau du package:
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:Et si vous n'êtes pas prêt à publier
Baz
dans l'API de niveau supérieur, dans votre niveau supérieur,__init__.py
vous pourriez avoir:et si vos utilisateurs sont conscients de la disponibilité de
Baz
, ils peuvent l'utiliser:mais s'ils ne le savent pas, d'autres outils (comme pydoc ) ne les informeront pas.
Vous pouvez plus tard changer cela quand
Baz
est prêt pour les heures de grande écoute: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, enctypes/__init__.py
: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__
sensIl est logique de s'en tenir à la
_
convention de préfixe au lieu de__all__
:Un
export
décorateurL'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:
puis, où vous définiriez un
__all__
, vous faites ceci:Et cela fonctionne bien, qu'il soit exécuté en tant que principal ou importé par une autre fonction.
Et le provisionnement d'API avec
import *
fonctionnera également:la source
@export
décorateur.__init__.py
et l'utilisation de__all__
__all__
c'est correct.__all__
- mais alors je dirais que vous avez une API instable ... Ce serait quelque chose pour avoir des tests d'acceptation complets.module_1
etmodule_2
; est-il OK d'inclure un explicitedel module_1
dans__init__.py
? Ai-je tort de penser que cela en vaut la peine?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__.py
fichiers, il s'agit donc de packages python .Généralement,
__all__
n'entre en jeu que lorsque lafrom xxx import *
variante de l'import
instruction 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 utilisonsfrom package import *
.La grande différence est que lorsque vous omettez la déclaration de
__all__
dans un package__init__.py
, l'instructionfrom 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.la source
from package import *
importera toujours tout ce qui est défini dans__init__.py
, même s'il n'y en a pasall
. La différence importante est que sans__all__
cela, il n'importera pas automatiquement les modules définis dans le répertoire du package.Cela change également ce que pydoc affichera:
module1.py
module2.py
$ pydoc module1
$ pydoc module2
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.la source
__all__
personnalise*
enfrom <module> import *
__all__
personnalise*
enfrom <package> import *
Un module est un
.py
fichier destiné à être importé.Un package est un répertoire avec un
__init__.py
fichier. Un package contient généralement des modules.MODULES
__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
swiss
etcheddar
sont introduits dans l'espace de noms local, mais pasgouda
: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
à partir des noms d' importation de module
module d' importation en tant que nom local
PAQUETS
Dans le
__init__.py
fichier 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
: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__.py
sont importés:[ PEP 8 , @ToolmakerSteve]
la source
from <package> import *
sans__all__
en__init__.py
qui n'importer l' un des modules .__init__.py
s'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?Du (Un Unofficial) Wiki de Référence Python :
la source
__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 :
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: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 :la source
Réponse courte
__all__
affecte lesfrom <module> import *
déclarations.Longue réponse
Considérez cet exemple:
Dans
foo/__init__.py
:(Implicite) Si nous ne définissons pas
__all__
, alorsfrom foo import *
importera uniquement les noms définis dansfoo/__init__.py
.(Explicite) Si nous définissons
__all__ = []
, alorsfrom foo import *
n'importera rien.(Explicite) Si nous définissons
__all__ = [ <name1>, ... ]
, alorsfrom 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 .
la source
__all__
affecte la façon dontfrom 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 unefrom
instruction:Les
*
demandes que tous les attributs du modulefoo
(à 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'ilfoo
a un attribut__all__
, la valeur de l'attribut est la liste des noms liés par ce type defrom
déclaration.Si
foo
est un package et qu'il__init__.py
définit une liste nommée__all__
, il est considéré comme la liste des noms de sous-modules qui doivent être importés lorsqu'ilfrom foo import *
est rencontré. Si__all__
n'est pas défini, l'instructionfrom 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'import
instruction , 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:Voir aussi Pourquoi «importer *» est-il mauvais?
la source
Ceci est défini dans PEP8 ici :
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.
la source