Impossible d'importer Python à partir d'un autre dossier

90

Je n'arrive pas à convaincre Python d'importer un module dans un sous-dossier. J'obtiens l'erreur lorsque j'essaie de créer une instance de la classe à partir du module importé, mais l'importation elle-même réussit. Voici ma structure de répertoires:

Server
    -server.py
    -Models
        --user.py

Voici le contenu de server.py:

from sys import path
from os import getcwd
path.append(getcwd() + "\\models") #Yes, i'm on windows
print path
import user

u=user.User() #error on this line

Et user.py:

class User(Entity):
    using_options(tablename='users')

    username = Field(String(15))
    password = Field(String(64))
    email    = Field(String(50))
    status   = Field(Integer)
    created  = Field(DateTime)

L'erreur est: AttributeError: l'objet 'module' n'a pas d'attribut 'User'

Ryeguy
la source
1
Pouvez-vous coller le message d'erreur?
Harley Holcombe
Si depuis user.py, je souhaite importer server.py. Que fais-je?
Chandra Kanth

Réponses:

139

Je pense que vous devez créer un fichier appelé __init__.pydans le répertoire Models afin que python le traite comme un module.

Ensuite, vous pouvez faire:

from Models.user import User

Vous pouvez inclure du code dans le __init__.py(par exemple le code d'initialisation dont certaines classes différentes ont besoin) ou le laisser vide. Mais ça doit être là.

Dana
la source
2
Merci, je n'avais jamais entendu parler de paquets avant cela.
ryeguy
1
En fait, lors de l'importation, Python traite blah.py et et blah / __ init__.py exactement de la même manière.
pi.
2
Ce n'était pas évident ... Et je me dis: RTFM FMF
Alexander.Iljushkin
1
Notez que cela ne fonctionnera pas si vous exécutez server.py à partir d'un autre répertoire.
kchoi
1
et si server.py est dans un autre dossier?
Mandroid
24

Vous devez créer __init__.pysur le Modelssous - dossier. Le fichier est peut-être vide. Il définit un package.

Ensuite, vous pouvez faire:

from Models.user import User

Lisez tout à ce sujet dans le didacticiel python, ici .

Il y a aussi un bon article sur l'organisation des fichiers des projets python ici .

nosklo
la source
Que faire si le fichier n'est PAS vide?
darkgaze
12

importer un utilisateur

u = user.User () #error sur cette ligne

En raison du manque de __init__ mentionné ci-dessus, vous vous attendriez à une ImportError qui clarifierait le problème.

Vous n'en obtenez pas car «user» est également un module existant dans la bibliothèque standard. Votre instruction d'importation saisit celui-là et essaie de trouver la classe User à l'intérieur; cela n'existe pas et ce n'est qu'alors que vous obtenez l'erreur.

C'est généralement une bonne idée de rendre votre importation absolue:

import Server.Models.user

pour éviter ce genre d'ambiguïté. En effet, à partir de Python 2.7, 'import user' ne ressemblera pas du tout au module actuel.

Si vous voulez vraiment des importations relatives, vous pouvez les avoir explicitement dans Python 2.5 et plus en utilisant la syntaxe quelque peu laide:

from .user import User
bobince
la source
9

La bonne façon d'importer un module situé sur un dossier parent, lorsque vous n'avez pas de structure de package standard, est:

import os, sys
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(CURRENT_DIR))

(vous pouvez fusionner les deux dernières lignes mais cette façon est plus facile à comprendre).

Cette solution est multiplateforme et est suffisamment générale pour ne pas devoir être modifiée dans d'autres circonstances.

glarrain
la source
7

Il vous manque __init__.py. À partir du didacticiel Python:

Les fichiers __init__.py sont nécessaires pour que Python traite les répertoires comme contenant des packages; ceci est fait pour empêcher les répertoires avec un nom commun, tel que string, de masquer involontairement les modules valides qui surviennent plus tard sur le chemin de recherche du module. Dans le cas le plus simple, __init__.py peut simplement être un fichier vide, mais il peut également exécuter du code d'initialisation pour le package ou définir la variable __all__, décrite plus loin.

Mettez un fichier vide nommé __init__.py dans votre répertoire Models, et tout devrait être en or.

Harper Shelby
la source
1

comment écrivez-vous les paramètres os.path.dirname.... commande?

import os, sys
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(CURRENT_DIR))
utilisateur3440815
la source
0

Ma méthode préférée est d'avoir __init__.py sur chaque répertoire qui contient des modules qui sont utilisés par d'autres modules, et au point d'entrée, remplacez sys.path comme ci-dessous:

def get_path(ss):
  return os.path.join(os.path.dirname(__file__), ss)
sys.path += [
  get_path('Server'), 
  get_path('Models')
]

Cela rend les fichiers dans les répertoires spécifiés visibles pour l'importation, et je peux importer l'utilisateur à partir de Server.py.

kchoi
la source
0

Après avoir parcouru les réponses données par ces contributeurs ci-dessus - Zorglub29, Tom, Mark, Aaron McMillin, lucasamaral, JoeyZhao, Kjeld Flarup, Procyclinsur, martin.zaenker, tooty44 et déboguer le problème auquel j'étais confronté, j'ai découvert un cas d'utilisation différent en raison auquel je faisais face à ce problème. D'où l'ajout de mes observations ci-dessous pour la référence de quiconque.

Dans mon code, j'avais une importation cyclique de classes. Par exemple:

src
 |-- utilities.py (has Utilities class that uses Event class)  
 |-- consume_utilities.py (has Event class that uses Utilities class)
 |-- tests
      |-- test_consume_utilities.py (executes test cases that involves Event class)

J'ai eu l'erreur suivante lorsque j'ai essayé d'exécuter python -m pytest tests / test_utilities.py pour exécuter des UT écrits dans test_utilities.py.

ImportError while importing test module '/Users/.../src/tests/test_utilities.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_utilities.py:1: in <module>
    from utilities import Utilities
...
...
E   ImportError: cannot import name 'Utilities'

La façon dont j'ai résolu l'erreur était de re-factoriser mon code pour déplacer la fonctionnalité dans la classe d'importation cyclique afin que je puisse supprimer l'importation cyclique de classes.

Remarque, j'ai un __init__.pyfichier dans mon dossier ' src ' ainsi que dans mon dossier ' tests ' et j'ai toujours pu me débarrasser de ' ImportError » simplement en re-factorisant le code.

Le lien stackoverflow suivant fournit beaucoup plus de détails sur la dépendance circulaire en Python .

Shekharlondhe
la source