Je souhaite importer une fonction à partir d'un autre fichier dans le même répertoire.
Parfois, cela fonctionne pour moi, from .mymodule import myfunction
mais parfois je reçois un:
SystemError: Parent module '' not loaded, cannot perform relative import
Parfois ça marche avec from mymodule import myfunction
, mais parfois j'obtiens aussi un:
SystemError: Parent module '' not loaded, cannot perform relative import
Je ne comprends pas la logique ici, et je n'ai trouvé aucune explication. Cela semble complètement aléatoire.
Quelqu'un pourrait-il m'expliquer quelle est la logique derrière tout cela?
python
python-3.x
python-import
John Smith facultatif
la source
la source
Réponses:
Il est assez courant d'avoir une disposition comme celle-ci ...
... avec un
mymodule.py
comme ça ...... un
myothermodule.py
comme ça ...... et un
main.py
comme ça ...... qui fonctionne bien lorsque vous exécutez
main.py
oumypackage/mymodule.py
, mais échoue avecmypackage/myothermodule.py
, en raison de l'importation relative ...La façon dont vous êtes censé l'exécuter est ...
... mais c'est un peu verbeux, et ne se mélange pas bien avec une ligne de shebang comme
#!/usr/bin/env python3
.La solution la plus simple pour ce cas, en supposant que le nom
mymodule
est unique au monde, serait d'éviter d'utiliser des importations relatives et d'utiliser simplement ...... bien que, s'il n'est pas unique ou que la structure de votre package soit plus complexe, vous devrez inclure le répertoire contenant le répertoire de votre package
PYTHONPATH
et le faire comme ceci ...... ou si vous voulez que cela fonctionne "out of the box", vous pouvez d'abord frober le
PYTHONPATH
code in avec ceci ...C'est une sorte de douleur, mais il y a une idée de pourquoi dans un e-mail écrit par un certain Guido van Rossum ...
Que l'exécution de scripts à l'intérieur d'un package soit un contre-modèle ou non est subjectif, mais personnellement je le trouve vraiment utile dans un package que je possède qui contient des widgets wxPython personnalisés, donc je peux exécuter le script pour n'importe lequel des fichiers source pour afficher un
wx.Frame
contenant uniquement ce widget à des fins de test.la source
os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
si vousfile
êtes sûr que votre module a toujours un bon que vous pourriez également utiliseros.path.realpath(os.path.dirname(__file__))
.sys.path.append( os.path.join( os.path.dirname(__file__), os.path.pardir ) )
...which I've always seen as an antipattern.
Je ne vois pas comment c'est un anti pattern ... Il semble qu'il serait super pratique de simplement faire fonctionner intuitivement les importations relatives. Je veux juste pouvoir importer des choses dont je sais qu'elles se trouvent dans le même répertoire. Je me demande quel était son raisonnementExplication
À partir de PEP 328
À un moment donné, le PEP 338 est entré en conflit avec le PEP 328 :
et pour résoudre le problème, le PEP 366 a introduit la variable de niveau supérieur
__package__
:(c'est moi qui souligne)
Si
__name__
est'__main__'
,__name__.rpartition('.')[0]
renvoie une chaîne vide. C'est pourquoi il y a un littéral de chaîne vide dans la description de l'erreur:La partie pertinente de la
PyImport_ImportModuleLevelObject
fonction de CPython :CPython lève cette exception s'il n'a pas pu trouver
package
(le nom du package) dansinterp->modules
(accessible en tant quesys.modules
). Puisqu'ilsys.modules
s'agit d' un "dictionnaire qui mappe les noms des modules aux modules qui ont déjà été chargés" , il est désormais clair que le module parent doit être explicitement importé en absolu avant d'effectuer une importation relative .Remarque: Le correctif du problème 18018 a ajouté un autre
if
bloc , qui sera exécuté avant le code ci-dessus:Si
package
(comme ci-dessus) est une chaîne vide, le message d'erreur seraCependant, vous ne verrez cela qu'en Python 3.6 ou plus récent.
Solution n ° 1: exécutez votre script à l'aide de -m
Considérez un répertoire (qui est un package Python ):
Tous les fichiers du package commencent par les 2 mêmes lignes de code:
J'inclus ces deux lignes uniquement pour rendre l'ordre des opérations évident. Nous pouvons les ignorer complètement, car ils n'affectent pas l'exécution.
__init__.py et module.py ne contiennent que ces deux lignes (c'est-à-dire qu'elles sont effectivement vides).
standalone.py tente en outre d'importer module.py via l'importation relative:
Nous savons bien que
/path/to/python/interpreter package/standalone.py
cela échouera. Cependant, nous pouvons exécuter le module avec l'-m
option de ligne de commande qui "rechercherasys.path
le module nommé et exécutera son contenu en tant que__main__
module" :-m
effectue toutes les opérations d'importation pour vous et définit automatiquement__package__
, mais vous pouvez le faire vous-même dans leSolution n ° 2: définissez __package__ manuellement
Veuillez le considérer comme une preuve de concept plutôt qu'une solution réelle. Il n'est pas bien adapté à une utilisation dans le code du monde réel.
PEP 366 a une solution de contournement à ce problème, cependant, il est incomplet, car le réglage
__package__
seul n'est pas suffisant. Vous allez devoir importer au moins N packages précédents dans la hiérarchie des modules, où N est le nombre de répertoires parents (par rapport au répertoire du script) qui sera recherché pour le module importé.Donc,
Ajoutez le répertoire parent du Nème prédécesseur du module actuel à
sys.path
Supprimer le répertoire du fichier actuel de
sys.path
Importez le module parent du module actuel en utilisant son nom complet
Définissez
__package__
le nom complet de 2Effectuer l'importation relative
J'emprunterai des fichiers à la Solution # 1 et ajouterai d'autres sous-packages:
Cette fois, standalone.py importera module.py à partir du package de package à l'aide de l'importation relative suivante
Nous devrons faire précéder cette ligne du code passe-partout pour que cela fonctionne.
Il nous permet d'exécuter standalone.py par nom de fichier:
Une solution plus générale enveloppée dans une fonction peut être trouvée ici . Exemple d'utilisation:
Solution n ° 3: utilisez les importations absolues et les setuptools
Les étapes sont -
Remplacer les importations relatives explicites par des importations absolues équivalentes
Installer
package
pour le rendre importablePar exemple, la structure du répertoire peut être la suivante
où setup.py est
Les autres fichiers ont été empruntés à la solution n ° 1 .
L'installation vous permettra d'importer le package quel que soit votre répertoire de travail (en supposant qu'il n'y aura aucun problème de dénomination).
Nous pouvons modifier standalone.py pour utiliser cet avantage (étape 1):
Modifiez votre répertoire de travail
project
et exécutez/path/to/python/interpreter setup.py install --user
(--user
installe le package dans votre répertoire site-packages ) (étape 2):Vérifions qu'il est désormais possible d'exécuter standalone.py en tant que script:
Remarque : Si vous décidez de suivre cette voie, il serait préférable d'utiliser des environnements virtuels pour installer les packages de manière isolée.
Solution n ° 4: utilisez des importations absolues et du code standard
Franchement, l'installation n'est pas nécessaire - vous pouvez ajouter du code passe-partout à votre script pour faire fonctionner les importations absolues.
Je vais emprunter des fichiers à la Solution # 1 et changer standalone.py :
Ajoutez le répertoire parent du package à
sys.path
avant d' essayer d'importer quoi que ce soit du package à l' aide d'importations absolues:Remplacez l'importation relative par l'importation absolue:
standalone.py s'exécute sans problème:
Je pense que je dois vous avertir: essayez de ne pas le faire, surtout si votre projet a une structure complexe.
En guise de note complémentaire , le PEP 8 recommande l'utilisation d'importations absolues, mais déclare que dans certains scénarios, les importations relatives explicites sont acceptables:
la source
__package__
manuellement si le nom est__main__
pour résoudre le problème?imp
module et le régler en__package__
conséquence, mais le résultat est clairement un anti-modèle.AttributeError: 'PosixPath' object has no attribute 'path'
.Mettez ceci dans le fichier __init__.py de votre paquet :
En supposant que votre colis est comme ceci:
Utilisez maintenant des importations régulières dans votre package, comme:
Cela fonctionne à la fois en python 2 et 3.
la source
__init__.py
solution résoudra essentiellement toutes les erreurs d'importation relatives.sys.path
car je crains que cela n'affecte un autre code. (En partie parce que je ne connais pas les subtilités de son fonctionnement.)J'ai rencontré ce problème. Une solution de contournement est importée via un bloc if / else comme suit:
la source
except:
est mauvais. utilisezexcept ImportError:
plutôt!SystemError
ici. (Py 3.4)if __name__ == '__main__': from mymod import as_int; else: from .mymod import as_int
.J'espère que cela sera utile à quelqu'un là-bas - j'ai parcouru une demi-douzaine de messages stackoverflow essayant de comprendre les importations relatives similaires à ce qui est affiché ci-dessus ici. J'ai tout configuré comme suggéré mais je frappais toujours
ModuleNotFoundError: No module named 'my_module_name'
Comme je ne faisais que développer localement et jouer, je n'avais pas créé / exécuté de
setup.py
fichier. Je n'avais pas non plus apparemment réglé monPYTHONPATH
.J'ai réalisé que lorsque j'ai exécuté mon code comme je l'avais été lorsque les tests étaient dans le même répertoire que le module, je n'ai pas pu trouver mon module:
Cependant, lorsque j'ai spécifié explicitement le chemin, les choses ont commencé à fonctionner:
Donc, si quelqu'un a essayé quelques suggestions, pense que son code est correctement structuré et se trouve toujours dans une situation similaire à la mienne, essayez l'une des opérations suivantes si vous n'exportez pas le répertoire en cours vers votre PYTHONPATH:
$ PYTHONPATH=. python3 test/my_module/module_test.py
PYTHONPATH=.
, créez unsetup.py
fichier avec un contenu comme celui-ci et exécutezpython setup.py development
pour ajouter des packages au chemin:la source
J'avais besoin d'exécuter python3 à partir du répertoire principal du projet pour le faire fonctionner.
Par exemple, si le projet a la structure suivante:
Solution
Je voudrais exécuter python3 dans le dossier project_demo / puis effectuer un
la source
Pour éviter ce problème, j'ai imaginé une solution avec le package de reconditionnement , qui fonctionne pour moi depuis un certain temps. Il ajoute le répertoire supérieur au chemin lib:
Le reconditionnement peut effectuer des importations relatives qui fonctionnent dans un large éventail de cas, en utilisant une stratégie intelligente (inspection de la pile d'appels).
la source
si les deux packages se trouvent dans votre chemin d'importation (sys.path) et que le module / classe que vous voulez est dans example / example.py, alors pour accéder à la classe sans importation relative, essayez:
la source
Je pense que la meilleure solution est de créer un package pour votre module: Voici plus d'informations sur la façon de le faire.
Une fois que vous avez un package dont vous n'avez pas à vous soucier de l'importation relative, vous pouvez simplement effectuer des importations absolues.
la source
J'ai eu un problème similaire: j'avais besoin d'un service Linux et d'un plugin cgi qui utilisent des constantes communes pour coopérer. La façon «naturelle» de le faire est de les placer dans le fichier init .py du paquet, mais je ne peux pas démarrer le plugin cgi avec le paramètre -m.
Ma solution finale était similaire à la solution n ° 2 ci-dessus:
L'inconvénient est que vous devez préfixer les constantes (ou fonctions communes) avec pkg:
la source