J'ai été ici:
- http://www.python.org/dev/peps/pep-0328/
- http://docs.python.org/2/tutorial/modules.html#packages
- Paquets Python: importations relatives
- le code d'exemple d'importation relative python ne fonctionne pas
- Réponse ultime aux importations de python relatives
- Importations relatives en Python
- Python: désactivation de l'importation relative
et de nombreuses URL que je n'ai pas copiées, certaines sur SO, d'autres sur d'autres sites, quand je pensais avoir rapidement la solution.
La question récurrente est la suivante: avec Windows 7, Python 2.7.3 32 bits, comment puis-je résoudre ce message "Tentative d'importation relative dans un non-package"? J'ai construit une réplique exacte du paquet sur pep-0328:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
Les importations ont été effectuées depuis la console.
J'ai fait des fonctions nommées spam et oeufs dans leurs modules appropriés. Naturellement, cela n'a pas fonctionné. La réponse est apparemment dans la 4ème URL que j'ai listée, mais ce sont tous des anciens pour moi. Il y a eu cette réponse sur l'une des URL que j'ai visitées:
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.
La réponse ci-dessus semble prometteuse, mais ce ne sont que des hiéroglyphes pour moi. Donc ma question, comment puis-je faire en sorte que Python ne me revienne pas "Tentative d'importation relative dans un non-package"? a une réponse qui implique -m, soi-disant.
Quelqu'un peut-il me dire pourquoi Python donne ce message d'erreur, ce qu'il signifie par «non-package», pourquoi et comment définissez-vous un «package», et la réponse précise formulée en termes assez faciles à comprendre pour un jardin d'enfants .
la source
def spam(): pass
, moduleA.py adef eggs(): pass
. J'ai essayé d'exécuter quelques commandes «à partir de quelque chose d'importer quelque chose», mais elles n'ont pas fonctionné. Encore une fois, voir pep-0328.from .something import something
dans l'interpréteur interactif, cela ne fonctionnera pas. Les importations relatives ne peuvent être utilisées que dans les modules, pas de manière interactive.Réponses:
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 faitespython -m myfile
, ou s'il est chargé lorsqu'uneimport
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:
si vous avez importé
moduleX
(note: importé , pas directement exécuté), son nom seraitpackage.subpackage1.moduleX
. Si vous importiezmoduleA
, son nom seraitpackage.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 faitesimport moduleX
, le nom demoduleX
sera simplementmoduleX
et nonpackage.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:
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 estpackage.subpackage1.moduleX
,..moduleA
cela signifieraitpackage.moduleA
. Pour que afrom .. 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 fairepython -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 exemplemyfile.py
, qui utilise des fonctions à l'intérieurmoduleX
. Si tel est le cas, placez-lemyfile.py
ailleurs - pas à l'intérieur dupackage
répertoire - et exécutez-le. Si à l'intérieurmyfile.py
vous faites des choses commefrom 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__
estNone
.)la source
Its name has no dots, and therefore you cannot use from .. import statements inside it. If you try to do so, you will get the "relative-import in non-package" error.
C'est fondamentalement dérangeant. Qu'est-ce qui est si difficile à regarder dans le répertoire actuel? Python devrait en être capable. Est-ce résolu dans la version 3x?__package__ = 'package.subpackage1'
ou similaire. Ensuite , ce fichier ne sera toujours considéré comme faisant partie de ce paquet même si exécuté directement. Si vous avez d'autres questions à__package__
votre sujet, vous voudrez peut-être poser une question distincte, car nous évitons ici le problème de votre question d'origine.__name__
etsys.path
. Plus précisément, avecpython -m pkg.mod
,__name__
est défini sur__main__
, nonpkg.mod
; les importations relatives sont résolues en utilisant__package__
plutôt que__name__
dans ce cas. En outre, Python ajoute le répertoire du script plutôt que le répertoire actuelsys.path
lors de l'exécutionpython path/to/script.py
; il ajoute le répertoire courantsys.path
lors de l'exécution de la plupart des autres façons, y comprispython -m pkg.mod
.C'est vraiment un problème avec python. L'origine de la confusion est que les gens prennent par erreur l'importation relative comme un chemin relatif qui ne l'est pas.
Par exemple, lorsque vous écrivez dans faa.py :
Cela n'a de sens que si faa.py a été identifié et chargé par python, pendant l'exécution, en tant que partie d'un package. Dans ce cas, le nom du module pour faa.py serait par exemple some_packagename.faa . Si le fichier a été chargé simplement parce qu'il se trouve dans le répertoire courant, lorsque python est exécuté, son nom ne fera référence à aucun package et, finalement, l'importation relative échouera.
Une solution simple pour référencer des modules dans le répertoire courant, est d'utiliser ceci:
la source
from __future__ import absolute_import
forcer l'utilisateur à utiliser votre code correctement ... pour que vous puissiez toujours le fairefrom . import foo
Voici une recette générale, modifiée pour correspondre à un exemple, que j'utilise actuellement pour traiter des bibliothèques Python écrites sous forme de packages, qui contiennent des fichiers interdépendants, où je veux pouvoir tester des parties d'entre elles au coup par coup. Appelons cela
lib.foo
et disons qu'il a besoin d'accéder auxlib.fileA
fonctionsf1
etf2
, etlib.fileB
à la classeClass3
.J'ai inclus quelques
print
appels pour illustrer comment cela fonctionne. En pratique, vous voudrez les supprimer (et peut-être aussi lafrom __future__ import print_function
ligne).Cet exemple particulier est trop simple pour montrer quand nous avons vraiment besoin d'insérer une entrée dans
sys.path
. (Voir Lars réponse pour un cas où nous ne besoin, quand nous avons deux ou plusieurs niveaux de répertoires de paquets, et nous utilisonsos.path.dirname(os.path.dirname(__file__))
-mais il n'a pas vraiment mal non plus .) Il est également assez sûr pour ce faire sansif _i in sys.path
tester. Cependant, si chaque fichier importé insère le même chemin - par exemple, si les deuxfileA
etfileB
que vous souhaitez importer des utilitaires à partir du package - cela encombresys.path
le même chemin plusieurs fois, il est donc agréable d'avoir leif _i not in sys.path
dans le passe-partout.L'idée ici est la suivante (et notez que tous fonctionnent de la même manière sur python2.7 et python 3.x):
Si exécuté en tant que
import lib
ou enfrom lib import foo
tant qu'importation de package standard à partir du code ordinaire,__package
estlib
et__name__
estlib.foo
. Nous prenons le premier chemin de code, important de.fileA
, etc.S'il est exécuté en tant que
python lib/foo.py
,__package__
sera None et le__name__
sera__main__
.Nous prenons le deuxième chemin de code. Le
lib
répertoire sera déjà danssys.path
donc il n'est pas nécessaire de l'ajouter. Nous importons defileA
, etc.S'il est exécuté dans le
lib
répertoire aspython foo.py
, le comportement est le même que pour le cas 2.S'il est exécuté dans le
lib
répertoire aspython -m foo
, le comportement est similaire aux cas 2 et 3. Cependant, le chemin d'accès aulib
répertoire n'est pas danssys.path
, nous l'ajoutons donc avant l'importation. La même chose s'applique si nous exécutons Python, puisimport foo
.(Puisque
.
c'est danssys.path
, nous n'avons pas vraiment besoin d'ajouter la version absolue du chemin ici. C'est là qu'une structure d'imbrication de paquets plus profonde, où nous voulons le fairefrom ..otherlib.fileC import ...
, fait une différence. Si vous ne le faites pas, vous pouvez omettre complètement toute lasys.path
manipulation.)Remarques
Il y a encore une bizarrerie. Si vous exécutez tout cela de l'extérieur:
ou:
le comportement dépend du contenu de
lib/__init__.py
. Si cela existe et est vide , tout va bien:Mais si
lib/__init__.py
lui - même importeroutine
pour pouvoir exporterroutine.name
directement en tant quelib.name
, vous obtenez:Autrement dit, le module est importé deux fois, une fois via le package, puis à nouveau de
__main__
manière à exécuter votremain
code. Python 3.6 et versions ultérieures en avertissent:L' avertissement est nouveau, mais le comportement averti ne l'est pas. Cela fait partie de ce que certains appellent le double piège à l'importation . (Pour plus de détails, voir le numéro 27487. ) Nick Coghlan dit:
Notez que bien que nous violions cette règle ici, nous le faisons uniquement lorsque le fichier en cours de chargement n'est pas chargé dans le cadre d'un package, et notre modification est spécifiquement conçue pour nous permettre d'accéder à d'autres fichiers de ce package. (Et, comme je l'ai noté, nous ne devrions probablement pas faire cela du tout pour les packages à niveau unique.) Si nous voulions être extra-propres, nous pourrions réécrire ceci comme, par exemple:
Autrement dit, nous modifions
sys.path
assez longtemps pour réaliser nos importations, puis nous le remettons tel quel (en supprimant une copie de_i
if et seulement si nous en avons ajouté une_i
).la source
Donc, après avoir abordé ce sujet avec beaucoup d'autres, je suis tombé sur une note publiée par Dorian B dans cet article qui a résolu le problème spécifique que je rencontrais pour développer des modules et des classes à utiliser avec un service Web, mais je veux aussi être capable de les tester pendant le codage, en utilisant les fonctionnalités de débogage de PyCharm. Pour exécuter des tests dans une classe autonome, j'inclurais les éléments suivants à la fin de mon fichier de classe:
mais si je voulais importer d'autres classes ou modules dans le même dossier, je devrais alors changer toutes mes instructions d'importation de la notation relative aux références locales (c'est-à-dire supprimer le point (.)) Mais après avoir lu la suggestion de Dorian, j'ai essayé la sienne ' one-liner 'et ça a marché! Je peux maintenant tester dans PyCharm et laisser mon code de test en place lorsque j'utilise la classe dans une autre classe en cours de test, ou lorsque je l'utilise dans mon service Web!
L'instruction if vérifie si nous exécutons ce module en tant que principal ou s'il est utilisé dans un autre module testé en tant que principal . C'est peut-être évident, mais j'offre cette note ici au cas où quelqu'un d'autre frustré par les problèmes d'importation relatifs ci-dessus pourrait en faire usage.
la source
Voici une solution que je ne recommanderais pas, mais qui pourrait être utile dans certaines situations où les modules n'ont tout simplement pas été générés:
la source
J'ai eu un problème similaire où je ne voulais pas changer le chemin de recherche du module Python et j'avais besoin de charger un module relativement à partir d'un script (malgré "les scripts ne peuvent pas importer relativement avec tous" comme BrenBarn l'a expliqué plus haut).
J'ai donc utilisé le hack suivant. Malheureusement, il s'appuie sur le
imp
module devenu obsolète depuis la version 3.4 pour être abandonné en faveur deimportlib
. (Est-ce possible avecimportlib
aussi? Je ne sais pas.) Pourtant, le hack fonctionne pour l'instant.Exemple pour accéder à des membres de
moduleX
àsubpackage1
partir d' un script résidant dans lesubpackage2
dossier:Une approche plus propre semble être de modifier le sys.path utilisé pour charger les modules comme mentionné par Federico.
la source
__name__
change selon que le code en question est exécuté dans l'espace de noms global ou dans le cadre d'un module importé.Si le code ne s'exécute pas dans l'espace global,
__name__
sera le nom du module. S'il s'exécute dans un espace de noms global - par exemple, si vous le saisissez dans une console ou exécutez le module en tant que script à l'aide de devientpython.exe yourscriptnamehere.py
alors .__name__
"__main__"
Vous verrez que beaucoup de code python
if __name__ == '__main__'
est utilisé pour tester si le code est exécuté à partir de l'espace de noms global - qui vous permet d'avoir un module qui se double d'un script.Avez-vous essayé de faire ces importations depuis la console?
la source
@ La réponse de BrenBarn dit tout, mais si vous êtes comme moi, cela peut prendre un certain temps à comprendre. Voici mon cas et comment la réponse de @ BrenBarn s'y applique, peut-être que cela vous aidera.
L'affaire
En utilisant notre exemple familier, et ajoutez-y que moduleX.py a une importation relative vers ..moduleA. Étant donné que j'ai essayé d'écrire un script de test dans le répertoire subpackage1 qui a importé moduleX, mais j'ai ensuite eu l'erreur redoutée décrite par l'OP.
Solution
Déplacez le script de test au même niveau que le package et importez package.subpackage1.moduleX
Explication
Comme expliqué, les importations relatives sont effectuées par rapport au nom actuel. Lorsque mon script de test importe moduleX à partir du même répertoire, le nom du module dans moduleX est moduleX. Lorsqu'il rencontre une importation relative, l'interpréteur ne peut pas sauvegarder la hiérarchie des packages car il est déjà en haut
Lorsque j'importe moduleX par le haut, le nom à l'intérieur de moduleX est package.subpackage1.moduleX et l'importation relative peut être trouvée
la source
A écrit un petit paquet python à PyPi qui pourrait aider les téléspectateurs de cette question. Le package agit comme une solution de contournement si l'on souhaite pouvoir exécuter des fichiers python contenant des importations contenant des packages de niveau supérieur à partir d'un package / projet sans être directement dans le répertoire du fichier d'importation. https://pypi.org/project/import-anywhere/
la source
Pour que Python ne me revienne pas "Tentative d'importation relative dans un non-package". paquet/
init .py subpackage1 / init .py moduleX.py moduleY.py subpackage2 / init .py moduleZ.py moduleA.py
Cette erreur se produit uniquement si vous appliquez une importation relative au fichier parent. Par exemple, le fichier parent retourne déjà main après avoir codé "print ( nom )" dans moduleA.py. Donc CE fichier est déjà principalil ne peut retourner aucun paquet parent plus loin. les importations relatives sont requises dans les fichiers des packages subpackage1 et subpackage2, vous pouvez utiliser ".." pour faire référence au répertoire parent ou au module. Mais le parent est que s'il est déjà de niveau supérieur, il ne peut pas aller plus loin que ce répertoire parent (package). Ces fichiers dans lesquels vous appliquez l'importation relative aux parents ne peuvent fonctionner qu'avec l'application de l'importation absolue. Si vous utilisez ABSOLUTE IMPORT IN PARENT PACKAGE NO ERROR viendra car python sait qui est au niveau supérieur du package même si votre fichier est dans des sous-packages en raison du concept de PYTHON PATH qui définit le niveau supérieur du projet
la source