Il semble qu'il y ait déjà pas mal de questions ici sur l'importation relative dans python 3, mais après avoir parcouru bon nombre d'entre elles, je n'ai toujours pas trouvé la réponse à mon problème. voici donc la question.
J'ai un package ci-dessous
package/
__init__.py
A/
__init__.py
foo.py
test_A/
__init__.py
test.py
et j'ai une seule ligne dans test.py:
from ..A import foo
maintenant, je suis dans le dossier de package
, et je lance
python -m test_A.test
J'ai un message
"ValueError: attempted relative import beyond top-level package"
mais si je suis dans le dossier parent de package
, par exemple, je lance:
cd ..
python -m package.test_A.test
tout va bien.
Maintenant, ma question est:
quand je suis dans le dossier de package
, et que j'exécute le module à l'intérieur du sous-package test_A car test_A.test
, selon ma compréhension, ..A
monte d'un seul niveau, qui est toujours dans le package
dossier, pourquoi il donne un message disant beyond top-level package
. Quelle est exactement la raison qui provoque ce message d'erreur?
Réponses:
EDIT: Il existe des réponses meilleures / plus cohérentes à cette question dans d'autres questions:
Pourquoi ça ne marche pas? C'est parce que python n'enregistre pas d'où un paquet a été chargé. Ainsi, lorsque vous le faites
python -m test_A.test
, il supprime simplement les connaissances quitest_A.test
sont réellement stockéespackage
(c'estpackage
-à- dire qu'il n'est pas considéré comme un package). Tenter,from ..A import foo
c'est essayer d'accéder à des informations dont il n'a plus (c'est-à-dire les répertoires frères d'un emplacement chargé). C'est conceptuellement similaire à autoriserfrom ..os import path
un fichier dansmath
. Ce serait mauvais car vous voulez que les packages soient distincts. S'ils ont besoin d'utiliser quelque chose d'un autre paquet, ils doivent s'y référer globalement avecfrom os import path
et laisser python déterminer où cela se trouve avec$PATH
et$PYTHONPATH
.Lorsque vous utilisez
python -m package.test_A.test
, l'utilisation defrom ..A import foo
resolves est très bien car il a gardé une trace de ce qui se trouvepackage
et vous accédez simplement à un répertoire enfant d'un emplacement chargé.Pourquoi python ne considère-t-il pas que le répertoire de travail actuel est un package? AUCUN INDICE , mais ça serait utile.
la source
-m
indicateur et de l'exécuter à partir du répertoire ci-dessus.sys.path
hack, mais l'utilisation de setuptools , ce qui est beaucoup plus intéressant à mon avis.Essaye ça. A travaillé pour moi.
la source
A/bar.py
existe et enfoo.py
vousfrom .bar import X
.Hypothèse:
si vous êtes dans le
package
répertoireA
et que vous aveztest_A
des packages distincts.Conclusion: les
..A
importations ne sont autorisées que dans un package.Notes supplémentaires:
Rendre les importations relatives uniquement disponibles dans les packages est utile si vous souhaitez forcer le placement des packages sur n'importe quel chemin situé sur
sys.path
.ÉDITER:
Le répertoire de travail actuel se trouve généralement dans sys.path. Ainsi, tous les fichiers sont importables. Il s'agit d'un comportement depuis Python 2 lorsque les packages n'existaient pas encore. Faire du répertoire en cours d'exécution un package permettrait d'importer des modules comme "import .A" et comme "import A" qui seraient alors deux modules différents. C'est peut-être une incohérence à considérer.
la source
python -m package.test_A.test
semble faire ce qui est voulu, et mon argument est que ce devrait être la valeur par défaut. Alors, pouvez-vous me donner un exemple de cette incohérence?#include
serait très utile!Aucune de ces solutions ne fonctionnait pour moi en 3.6, avec une structure de dossiers comme:
Mon objectif était d'importer du module1 vers le module2. Ce qui a finalement fonctionné pour moi, c'est assez curieusement:
Notez le point unique par opposition aux solutions à deux points mentionnées jusqu'à présent.
Edit: Les éléments suivants ont aidé à clarifier cela pour moi:
Dans mon cas, le répertoire de travail était (de manière inattendue) la racine du projet.
la source
sys.path.append(".")
travaillé parce que vous l'appelez dans le répertoire parent, notez qu'il.
représente toujours le répertoire dans lequel vous exécutez la commande python.from package.A import foo
Je pense que c'est plus clair que
la source
sys.path.append("..")
. testé sur python 3.6Comme le suggère la réponse la plus populaire, c'est essentiellement parce que votre
PYTHONPATH
ousys.path
inclut.
mais pas votre chemin vers votre package. Et l'importation relative est relative à votre répertoire de travail actuel, pas au fichier où l'importation se produit; bizarrement.Vous pouvez résoudre ce problème en modifiant d'abord votre importation relative en absolu, puis en le commençant par:
OU forcer le chemin python lorsqu'il est appelé de cette façon, car:
Avec
python -m test_A.test
vous exécuteztest_A/test.py
avec__name__ == '__main__'
et__file__ == '/absolute/path/to/test_A/test.py'
Cela signifie que
test.py
vous pouvez utiliser votreimport
semi-protégé absolu dans la condition de cas principale et également effectuer une manipulation de chemin Python unique:la source
Edit: 2020-05-08: Il semble que le site Web que j'ai cité ne soit plus contrôlé par la personne qui a écrit le conseil, donc je supprime le lien vers le site. Merci de m'avoir informé de baxx.
Si quelqu'un a encore du mal après les bonnes réponses déjà fournies, j'ai trouvé des conseils sur un site Web qui n'est plus disponible.
Citation essentielle du site que j'ai mentionné:
Il est assez évident que cela doit être ainsi, en y réfléchissant après coup. J'essayais d'utiliser sys.path.append ('..') dans mes tests, mais j'ai rencontré le problème signalé par OP. En ajoutant l'importation et la définition de sys.path avant mes autres importations, j'ai pu résoudre le problème.
la source
si vous en avez un
__init__.py
dans un dossier supérieur, vous pouvez initialiser l'importation commeimport file/path as alias
dans ce fichier init. Ensuite, vous pouvez l'utiliser sur des scripts inférieurs comme:la source
À mon humble avis, je comprends cette question de cette façon:
[CAS 1] Lorsque vous démarrez une importation absolue comme
ou
ou
vous définissez en fait l' import-ancre pour être
test_A
, en d'autres termes, le package de niveau supérieur esttest_A
. Donc, lorsque nous avons test.py dofrom ..A import xxx
, vous vous échappez de l'ancre, et Python ne le permet pas.[CAS 2] Quand vous le faites
ou
votre ancre devient
package
, cepackage/test_A/test.py
faisant,from ..A import xxx
n'échappe pas à l'ancre (toujours dans lepackage
dossier), et Python accepte cela avec plaisir.En bref:
En outre, nous pouvons utiliser le nom de module complet (FQMN) pour inspecter ce problème.
Vérifiez le FQMN dans chaque cas:
test.__name__
=package.test_A.test
test.__name__
=test_A.test
Ainsi, pour CASE2, un
from .. import xxx
entraînera un nouveau module avec FQMN =package.xxx
, ce qui est acceptable.Alors que pour CASE1, le
..
de l'intérieurfrom .. import xxx
sautera du nœud de départ (ancre) detest_A
, et cela n'est PAS autorisé par Python.la source
Pas sûr en python 2.x mais en python 3.6, en supposant que vous essayez d'exécuter toute la suite, il vous suffit d'utiliser
-t
Donc, sur une structure comme
On pourrait par exemple utiliser:
python3 unittest discover -s /full_path/project_root/tests -t /full_path/project_root/
Et toujours importer les
my_module.my_class
sans drames majeurs.la source