Importations relatives pour la milliardième fois

717

J'ai été ici:

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 .

Paul Roub
la source
5
Comment essayez-vous d'utiliser les fichiers que vous affichez? Quel est le code que vous utilisez?
BrenBarn
Voir python.org/dev/peps/pep-0328 . J'ai utilisé le format de package que j'ai décrit dans mon message. Les fichiers init .py sont vides. moduleY.py a def spam(): pass, moduleA.py a def 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.
6
Voir ma réponse. Vous n'avez toujours pas complètement clarifié ce que vous faites, mais si vous essayez de le faire from .something import somethingdans 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.
BrenBarn
105
Le simple fait que "des milliards" de personnes - ok 83 136 selon ce commentaire - éprouvent suffisamment de difficultés avec les importations pour rechercher cette question; nous pouvons seulement conclure que les importations de python sont contre-intuitives pour beaucoup, sinon la plupart des programmeurs. Guido, vous devriez peut-être accepter cela et demander à un comité de repenser le mécanisme d'importation. Au minimum, cette syntaxe devrait fonctionner si x.py et z.py sont dans le même répertoire. A savoir si x.py a la déclaration, "from .z import MyZebraClass" x devrait importer z MEME s'il est exécuté comme principal ! Pourquoi est-ce si difficile?
Steve L
4
Après avoir lu une grande partie de ce fil, bien que ce ne soit pas une réponse à la question, "il suffit d'utiliser les importations absolues" semble être la solution ...
CodeJockey

Réponses:

1043

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.pysur 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 importinstruction 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 à moduleApartir 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/subpackage1et que vous le faites import moduleX, le nom de moduleXsera simplement moduleXet 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 pythonet 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, ..moduleAcela signifierait package.moduleA. Pour que a from .. importfonctionne, le nom du module doit avoir au moins autant de points qu'il y en a dans l' importinstruction.

... 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 .. importinstructions à 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 moduleXou 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:

  1. Si vous voulez vraiment exécuter moduleXdirectement, 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 -mdit à Python de le charger en tant que module, pas en tant que script de niveau supérieur.

  2. 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 packagerépertoire - et exécutez-le. Si à l'intérieur myfile.pyvous 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 ( packagedans 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.)

BrenBarn
la source
62
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?
7
@Stopforgettingmyaccounts ...: PEP 366 montre comment cela fonctionne. À l'intérieur d'un fichier, vous pouvez faire __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.
BrenBarn
109
Cela devrait être la réponse à toutes les questions relatives aux importations relatives de Python. Cela devrait être dans les documents, même.
edsioufi
10
Voir python.org/dev/peps/pep-0366 - "Notez que ce passe-partout est suffisant uniquement si le package de niveau supérieur est déjà accessible via sys.path. Un code supplémentaire qui manipule sys.path serait nécessaire pour une exécution directe pour fonctionner sans que le package de niveau supérieur ne soit déjà importable. " - c'est le bit le plus dérangeant pour moi car ce "code supplémentaire" est en fait assez long et ne peut pas être stocké ailleurs dans le package pour fonctionner facilement.
Michael Scott Cuthbert
14
Cette réponse est actuellement hors sur quelques détails importants concernant __name__et sys.path. Plus précisément, avec python -m pkg.mod, __name__est défini sur __main__, non pkg.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 actuel sys.pathlors de l'exécution python path/to/script.py; il ajoute le répertoire courant sys.pathlors de l'exécution de la plupart des autres façons, y compris python -m pkg.mod.
user2357112 prend en charge Monica
42

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 :

from .. import foo

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:

if __package__ is None or __package__ == '':
    # uses current directory visibility
    import foo
else:
    # uses current package visibility
    from . import foo
Rami Ka.
la source
6
La bonne solution est de from __future__ import absolute_importforcer l'utilisateur à utiliser votre code correctement ... pour que vous puissiez toujours le fairefrom . import foo
Giacomo Alzetta
@Giacomo: la réponse absolument juste à mon problème. Merci!
Fábio
8

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.fooet disons qu'il a besoin d'accéder aux lib.fileAfonctions f1et f2, et lib.fileBà la classe Class3.

J'ai inclus quelques printappels pour illustrer comment cela fonctionne. En pratique, vous voudrez les supprimer (et peut-être aussi la from __future__ import print_functionligne).

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 utilisons os.path.dirname(os.path.dirname(__file__))-mais il n'a pas vraiment mal non plus .) Il est également assez sûr pour ce faire sans if _i in sys.pathtester. Cependant, si chaque fichier importé insère le même chemin - par exemple, si les deux fileAet fileBque vous souhaitez importer des utilitaires à partir du package - cela encombre sys.pathle même chemin plusieurs fois, il est donc agréable d'avoir le if _i not in sys.pathdans le passe-partout.

from __future__ import print_function # only when showing how this works

if __package__:
    print('Package named {!r}; __name__ is {!r}'.format(__package__, __name__))
    from .fileA import f1, f2
    from .fileB import Class3
else:
    print('Not a package; __name__ is {!r}'.format(__name__))
    # these next steps should be used only with care and if needed
    # (remove the sys.path manipulation for simple cases!)
    import os, sys
    _i = os.path.dirname(os.path.abspath(__file__))
    if _i not in sys.path:
        print('inserting {!r} into sys.path'.format(_i))
        sys.path.insert(0, _i)
    else:
        print('{!r} is already in sys.path'.format(_i))
    del _i # clean up global name space

    from fileA import f1, f2
    from fileB import Class3

... all the code as usual ...

if __name__ == '__main__':
    import doctest, sys
    ret = doctest.testmod()
    sys.exit(0 if ret.failed == 0 else 1)

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):

  1. Si exécuté en tant que import libou en from lib import footant qu'importation de package standard à partir du code ordinaire, __packageest libet __name__est lib.foo. Nous prenons le premier chemin de code, important de .fileA, etc.

  2. 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 librépertoire sera déjà dans sys.pathdonc il n'est pas nécessaire de l'ajouter. Nous importons de fileA, etc.

  3. S'il est exécuté dans le librépertoire as python foo.py, le comportement est le même que pour le cas 2.

  4. S'il est exécuté dans le librépertoire as python -m foo, le comportement est similaire aux cas 2 et 3. Cependant, le chemin d'accès au librépertoire n'est pas dans sys.path, nous l'ajoutons donc avant l'importation. La même chose s'applique si nous exécutons Python, puis import foo.

    (Puisque . c'est dans sys.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 faire from ..otherlib.fileC import ..., fait une différence. Si vous ne le faites pas, vous pouvez omettre complètement toute la sys.pathmanipulation.)

Remarques

Il y a encore une bizarrerie. Si vous exécutez tout cela de l'extérieur:

$ python2 lib.foo

ou:

$ python3 lib.foo

le comportement dépend du contenu de lib/__init__.py. Si cela existe et est vide , tout va bien:

Package named 'lib'; __name__ is '__main__'

Mais si lib/__init__.py lui - même importe routinepour pouvoir exporter routine.namedirectement en tant que lib.name, vous obtenez:

$ python2 lib.foo
Package named 'lib'; __name__ is 'lib.foo'
Package named 'lib'; __name__ is '__main__'

Autrement dit, le module est importé deux fois, une fois via le package, puis à nouveau de __main__manière à exécuter votre maincode. Python 3.6 et versions ultérieures en avertissent:

$ python3 lib.routine
Package named 'lib'; __name__ is 'lib.foo'
[...]/runpy.py:125: RuntimeWarning: 'lib.foo' found in sys.modules
after import of package 'lib', but prior to execution of 'lib.foo';
this may result in unpredictable behaviour
  warn(RuntimeWarning(msg))
Package named 'lib'; __name__ is '__main__'

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:

Ce piège suivant existe dans toutes les versions actuelles de Python, y compris 3.3, et peut être résumé dans la directive générale suivante: "N'ajoutez jamais un répertoire de package, ou tout répertoire à l'intérieur d'un package, directement au chemin Python".

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:

    import os, sys
    _i = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    if _i not in sys.path:
        sys.path.insert(0, _i)
    else:
        _i = None

    from sub.fileA import f1, f2
    from sub.fileB import Class3

    if _i:
        sys.path.remove(_i)
    del _i

Autrement dit, nous modifions sys.pathassez longtemps pour réaliser nos importations, puis nous le remettons tel quel (en supprimant une copie de _iif et seulement si nous en avons ajouté une _i).

torek
la source
7

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:

if __name__ == '__main__':
   # run test code here...

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!

# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__main__':
    from codex import Codex # these are in same folder as module under test!
    from dblogger import DbLogger
else:
    from .codex import Codex
    from .dblogger import DbLogger

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.

Steve L
la source
1
Cela le résout en fait. Mais c'est vraiment méchant. Pourquoi n'est-ce pas le comportement par défaut?!
lo tolmencre
4

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:

import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/your_dir")
import your_script
your_script.a_function()
Federico
la source
2

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 impmodule devenu obsolète depuis la version 3.4 pour être abandonné en faveur de importlib. (Est-ce possible avec importlibaussi? Je ne sais pas.) Pourtant, le hack fonctionne pour l'instant.

Exemple pour accéder à des membres de moduleXà subpackage1partir d' un script résidant dans le subpackage2dossier:

#!/usr/bin/env python3

import inspect
import imp
import os

def get_script_dir(follow_symlinks=True):
    """
    Return directory of code defining this very function.
    Should work from a module as well as from a script.
    """
    script_path = inspect.getabsfile(get_script_dir)
    if follow_symlinks:
        script_path = os.path.realpath(script_path)
    return os.path.dirname(script_path)

# loading the module (hack, relying on deprecated imp-module)
PARENT_PATH = os.path.dirname(get_script_dir())
(x_file, x_path, x_desc) = imp.find_module('moduleX', [PARENT_PATH+'/'+'subpackage1'])
module_x = imp.load_module('subpackage1.moduleX', x_file, x_path, x_desc)

# importing a function and a value
function = module_x.my_function
VALUE = module_x.MY_CONST

Une approche plus propre semble être de modifier le sys.path utilisé pour charger les modules comme mentionné par Federico.

#!/usr/bin/env python3

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    # __file__ should be defined in this case
    PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
   sys.path.append(PARENT_DIR)
from subpackage1.moduleX import *
Lars
la source
Cela semble mieux ... tant pis, il vous faut toujours incorporer le nom du répertoire parent dans le fichier ... peut-être que cela peut être amélioré avec importlib. Peut-être que l'importlib peut même être monkeypatché pour que l'importation relative "fonctionne" pour les cas d'utilisation simples. Je vais m'y attaquer.
Andrew Wagner
J'utilise cependant python 2.7.14. Est-ce que quelque chose comme ça fonctionnerait encore?
user3474042
Je viens de tester les deux approches sur python 2.7.10 et elles ont bien fonctionné pour moi. En fait, vous n'avez pas le problème d'un module imp obsolète en 2.7, donc tant mieux.
Lars
2

__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 devient python.exe yourscriptnamehere.pyalors . __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?

théodoxe
la source
Ah, vous mentionnez donc -m. Cela fait que votre module s'exécute comme un script - si vous collez un if __name__ == '__main__' là-dedans, vous devriez voir que c'est '__main__' à cause de -m. Essayez simplement d'importer votre module dans un autre module afin que ce ne soit pas le niveau supérieur ... qui devrait vous permettre de faire l'importation relative
theodox
J'ai essayé de faire ces importations depuis la console, le fichier actif étant le bon module.
@Stopforgettingmyaccounts ...: Que voulez-vous dire par "fichier actif"?
BrenBarn
J'utilise Pyscripter. J'étais dans moduleX.py lorsque j'ai exécuté ces importations: à partir de .moduleY, importez du spam et à partir de. importer ModuleY.
Ne pas importer .moduleY suivi de moduleY.spam ()?
theodox
2

@ 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

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

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

Brad Dre
la source
En espérant que vous puissiez me guider là-dessus. Dans le lien suivant, si vous passez au cas 3, il indique que la solution 1 n'est pas possible. Veuillez vérifier ceci et me le faire savoir. Cela m'aidera énormément. chrisyeh96.github.io/2017/08/08/…
variable
@variable il y a une faute de frappe dans le lien et je ne suis pas autorisé à modifier. J'ai regardé le cas 3 et je n'ai pas suivi exactement ce que vous vouliez dire. Lorsque j'ai essayé cet exemple en python 2, il n'y a eu aucun problème qui me fait penser que j'ai raté quelque chose. Vous devriez peut-être publier une nouvelle question, mais vous devez fournir un exemple plus clair. Le cas 4 touche à ce dont je parle dans ma réponse ici: vous ne pouvez pas monter un répertoire pour une importation relative À MOINS que l'interprète ne commence dans un répertoire parent
Brad Dre
Merci je me réfère à python 3 et ici la question stackoverflow.com/questions/58577767/…
variable
1

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.

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/

ec2604
la source
-2

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

Sakshi Jain
la source