Script contre module
Voici une explication. La version courte est qu'il y a une grande différence entre exécuter directement un fichier Python et importer ce fichier ailleurs. Le simple fait de savoir dans quel répertoire se trouve un fichier ne détermine pas dans quel package Python pense qu'il se trouve. Cela dépend, en outre, de la façon dont vous chargez le fichier dans Python (en exécutant ou en important).
Il existe deux façons de charger un fichier Python: en tant que script de niveau supérieur ou en tant que module. Un fichier est chargé en tant que script de niveau supérieur si vous l'exécutez directement, par exemple en tapant python myfile.py
sur la ligne de commande. Il est chargé en tant que module si vous le faites python -m myfile
, ou s'il est chargé lorsqu'une import
instruction est rencontrée dans un autre fichier. Il ne peut y avoir qu'un seul script de niveau supérieur à la fois; le script de niveau supérieur est le fichier Python que vous avez exécuté pour démarrer les choses.
Appellation
Lorsqu'un fichier est chargé, il reçoit un nom (qui est stocké dans son __name__
attribut). S'il a été chargé en tant que script de niveau supérieur, son nom est __main__
. S'il a été chargé en tant que module, son nom est le nom de fichier, précédé des noms des packages / sous-packages dont il fait partie, séparés par des points.
Ainsi, par exemple dans votre exemple:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleA.py
si vous avez importé moduleX
(note: importé , pas directement exécuté), son nom serait package.subpackage1.moduleX
. Si vous importiez moduleA
, son nom serait package.moduleA
. Cependant, si vous exécutez directement à moduleX
partir de la ligne de commande, son nom sera plutôt __main__
, et si vous exécutez directement à moduleA
partir de la ligne de commande, son nom sera __main__
. Lorsqu'un module est exécuté en tant que script de niveau supérieur, il perd son nom normal et son nom est à la place __main__
.
Accéder à un module NON via son package contenant
Il y a une ride supplémentaire: le nom du module dépend s'il a été importé "directement" du répertoire dans lequel il se trouve, ou importé via un package. Cela ne fait une différence que si vous exécutez Python dans un répertoire et essayez d'importer un fichier dans ce même répertoire (ou un sous-répertoire de celui-ci). Par exemple, si vous démarrez l'interpréteur Python dans le répertoire package/subpackage1
et que vous le faites import moduleX
, le nom de moduleX
sera simplement moduleX
et non package.subpackage1.moduleX
. En effet, Python ajoute le répertoire actuel à son chemin de recherche au démarrage; s'il trouve le module à importer dans le répertoire courant, il ne saura pas que ce répertoire fait partie d'un package et les informations du package ne feront pas partie du nom du module.
Un cas spécial est si vous exécutez l'interpréteur de manière interactive (par exemple, tapez simplement python
et commencez à entrer du code Python à la volée). Dans ce cas, le nom de cette session interactive est __main__
.
Voici maintenant la chose cruciale pour votre message d'erreur: si le nom d'un module n'a pas de points, il n'est pas considéré comme faisant partie d'un package . Peu importe où le fichier se trouve réellement sur le disque. Tout ce qui compte, c'est son nom, et son nom dépend de la façon dont vous l'avez chargé.
Regardez maintenant le devis que vous avez inclus dans votre question:
Les importations relatives utilisent l'attribut de nom d'un module pour déterminer la position de ce module dans la hiérarchie des packages. Si le nom du module ne contient aucune information sur le package (par exemple, il est défini sur `` principal ''), les importations relatives sont résolues comme si le module était un module de niveau supérieur, quel que soit l'emplacement du module sur le système de fichiers.
Importations relatives ...
Les importations relatives utilisent le nom du module pour déterminer où il se trouve dans un package. Lorsque vous utilisez une importation relative comme from .. import foo
, les points indiquent d'augmenter un certain nombre de niveaux dans la hiérarchie des packages. Par exemple, si le nom de votre module actuel est package.subpackage1.moduleX
, ..moduleA
cela signifierait package.moduleA
. Pour que a from .. import
fonctionne, le nom du module doit avoir au moins autant de points qu'il y en a dans l' import
instruction.
... ne sont relatifs que dans un package
Cependant, si le nom de votre module est __main__
, il n'est pas considéré comme étant dans un package. Son nom n'a pas de points, et vous ne pouvez donc pas utiliser d' from .. import
instructions à l'intérieur. Si vous essayez de le faire, vous obtiendrez l'erreur "relative-import in non-package".
Les scripts ne peuvent pas importer de parent
Ce que vous avez probablement fait, c'est que vous avez essayé d'exécuter moduleX
ou similaire à partir de la ligne de commande. Lorsque vous avez fait cela, son nom a été défini sur __main__
, ce qui signifie que les importations relatives en son sein échoueront, car son nom ne révèle pas qu'il se trouve dans un package. Notez que cela se produira également si vous exécutez Python à partir du même répertoire où se trouve un module, puis essayez d'importer ce module, car, comme décrit ci-dessus, Python trouvera le module dans le répertoire actuel "trop tôt" sans se rendre compte qu'il est partie d'un package.
Souvenez-vous également que lorsque vous exécutez l'interpréteur interactif, le "nom" de cette session interactive est toujours __main__
. Ainsi, vous ne pouvez pas effectuer d'importations relatives directement à partir d'une session interactive . Les importations relatives ne doivent être utilisées que dans les fichiers de module.
Deux solutions:
Si vous voulez vraiment exécuter moduleX
directement, mais vous voulez toujours qu'il soit considéré comme faisant partie d'un package, vous pouvez le faire python -m package.subpackage1.moduleX
. Le -m
dit à Python de le charger en tant que module, pas en tant que script de niveau supérieur.
Ou peut-être que vous ne voulez pas réellement exécuter moduleX
, vous voulez simplement exécuter un autre script, par exemple myfile.py
, qui utilise des fonctions à l'intérieur moduleX
. Si tel est le cas, placez-le myfile.py
ailleurs - pas à l'intérieur du package
répertoire - et exécutez-le. Si à l'intérieur myfile.py
vous faites des choses comme from package.moduleA import spam
ça, ça marchera bien.
Remarques
Pour l'une ou l'autre de ces solutions, le répertoire du package ( package
dans votre exemple) doit être accessible depuis le chemin de recherche du module Python ( sys.path
). Si ce n'est pas le cas, vous ne pourrez pas utiliser quoi que ce soit dans le package de manière fiable.
Depuis Python 2.6, le "nom" du module à des fins de résolution de package est déterminé non seulement par ses __name__
attributs mais également par l' __package__
attribut. C'est pourquoi j'évite d'utiliser le symbole explicite __name__
pour faire référence au "nom" du module. Depuis Python 2.6, le "nom" d'un module est effectivement __package__ + '.' + __name__
, ou juste __name__
s'il l' __package__
est None
.)