No puedo hacer que Python importe desde una carpeta diferente

90

Parece que no puedo hacer que Python importe un módulo en una subcarpeta. Recibo el error cuando intento crear una instancia de la clase desde el módulo importado, pero la importación en sí se realiza correctamente. Aquí está mi estructura de directorio:

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

Aquí está el contenido 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

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

El error es: AttributeError: el objeto 'módulo' no tiene atributo 'Usuario'

centeno
fuente
1
¿Puedes pegar el mensaje de error?
Harley Holcombe
Si es de user.py, quiero importar server.py. ¿Qué debo hacer?
Chandra Kanth

Respuestas:

142

Creo que necesita crear un archivo llamado __init__.pyen el directorio Modelos para que Python lo trate como un módulo.

Entonces puedes hacer:

from Models.user import User

Puede incluir código en el __init__.py(por ejemplo, el código de inicialización que necesitan algunas clases diferentes) o dejarlo en blanco. Pero debe estar ahí.

Dana
fuente
2
Gracias, nunca había oído hablar de paquetes antes de esto.
ryeguy
1
En realidad, al importar Python trata blah.py y blah / __ init__.py exactamente igual.
pi.
2
Eso no fue obvio ... Y me digo a mí mismo: RTFM FMF
Alexander.Iljushkin
1
Tenga en cuenta que esto no funcionará si está ejecutando server.py desde otro directorio.
kchoi
1
¿Qué pasa si server.py está en otra carpeta?
Mandroid
24

Tienes que crear __init__.pyen la Modelssubcarpeta. El archivo puede estar vacío. Define un paquete.

Entonces puedes hacer:

from Models.user import User

Lea todo sobre esto en el tutorial de Python, aquí .

También hay un buen artículo sobre la organización de archivos de proyectos de Python aquí .

nosklo
fuente
¿Qué pasa si el archivo NO está vacío?
mirada oscura
12

importar usuario

u = user.User () #error en esta línea

Debido a la falta de __init__ mencionada anteriormente, esperaría un ImportError que aclararía el problema.

No obtiene uno porque 'usuario' también es un módulo existente en la biblioteca estándar. Su declaración de importación toma ese e intenta encontrar la clase de usuario dentro de él; que no existe y solo entonces aparece el error.

Por lo general, es una buena idea hacer que su importación sea absoluta:

import Server.Models.user

para evitar este tipo de ambigüedad. De hecho, desde Python 2.7, el 'usuario de importación' no se verá en relación con el módulo actual en absoluto.

Si realmente desea importaciones relativas, puede tenerlas explícitamente en Python 2.5 y versiones posteriores usando la sintaxis algo fea:

from .user import User
bobince
fuente
9

La forma correcta de importar un módulo ubicado en una carpeta principal, cuando no tiene una estructura de paquete estándar, es:

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

(puede fusionar las dos últimas líneas, pero de esta manera es más fácil de entender).

Esta solución es multiplataforma y es lo suficientemente general como para no necesitar modificaciones en otras circunstancias.

glarrain
fuente
7

Falta __init__.py. Del tutorial de Python:

Los archivos __init__.py son necesarios para que Python trate los directorios como si fueran paquetes; Esto se hace para evitar que los directorios con un nombre común, como cadena, oculten involuntariamente módulos válidos que aparecen más adelante en la ruta de búsqueda del módulo. En el caso más simple, __init__.py puede ser simplemente un archivo vacío, pero también puede ejecutar el código de inicialización del paquete o establecer la variable __all__, que se describe más adelante.

Coloque un archivo vacío llamado __init__.py en su directorio de Modelos, y todo debería ser dorado.

Harper Shelby
fuente
1

¿Cómo se escribe el os.path.dirnamecomando parámetros ....?

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

Mi forma preferida es tener __init__.py en cada directorio que contiene módulos que son utilizados por otros módulos, y en el punto de entrada, anular sys.path como se muestra a continuación:

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

Esto hace que los archivos en directorios especificados sean visibles para su importación, y puedo importar usuarios desde Server.py.

kchoi
fuente
0

Después de revisar las respuestas dadas por estos colaboradores anteriores: Zorglub29, Tom, Mark, Aaron McMillin, lucasamaral, JoeyZhao, Kjeld Flarup, Procyclinsur, martin.zaenker, tooty44 y depurar el problema al que me enfrentaba, descubrí un caso de uso diferente debido a lo que estaba enfrentando este problema. Por lo tanto, agrego mis observaciones a continuación para referencia de cualquiera.

En mi código tenía una importación cíclica de clases. Por ejemplo:

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)

Recibí el siguiente error cuando intenté ejecutar python -m pytest tests / test_utilities.py para ejecutar UT escritas en 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 forma en que resolví el error fue re-factorizando mi código para mover la funcionalidad en la clase de importación cíclica para poder eliminar la importación cíclica de clases.

Tenga en cuenta que tengo un __init__.pyarchivo en mi carpeta ' src ', así como en la carpeta ' tests ' y aún así pude deshacerme del ' ImportError ' simplemente volviendo a factorizar el código.

El siguiente enlace de stackoverflow proporciona muchos más detalles sobre la dependencia circular en Python .

shekharlondhe
fuente