Imagina esta estructura de directorios:
app/
__init__.py
sub1/
__init__.py
mod1.py
sub2/
__init__.py
mod2.py
Estoy codificando mod1
, y necesito importar algo de mod2
. ¿Cómo debería hacerlo?
Intenté from ..sub2 import mod2
pero obtengo un "Intento de importación relativa en un no paquete".
Busqué en Google pero encontré solo hacks "sys.path
manipulation". ¿No hay una manera limpia?
Edición: todos mis __init__.py
están actualmente vacíos
Edit2: Intento hacer esto porque sub2 contiene clases que se comparten entre subpaquetes (sub1
, subX
, etc.).
Edit3: El comportamiento que estoy buscando es el mismo que se describe en PEP 366 (gracias John B)
Todo el mundo parece querer decirte lo que debes hacer en lugar de solo responder la pregunta.
El problema es que está ejecutando el módulo como '__main__' al pasar el mod1.py como un argumento al intérprete.
Desde PEP 328 :
Las importaciones relativas utilizan el atributo __de un módulo para determinar la posición de ese módulo en la jerarquía de paquetes. Si el nombre del módulo no contiene ninguna información del paquete (por ejemplo, está configurado como '__main__'), las importaciones relativas se resuelven como si el módulo fuera un módulo de nivel superior, independientemente de dónde se encuentre el módulo en el sistema de archivos.
En Python 2.6, están agregando la capacidad de referenciar módulos en relación con el módulo principal. PEP 366 describe el cambio.
Actualizar : Según Nick Coghlan, la alternativa recomendada es ejecutar el módulo dentro del paquete utilizando el modificador -m.
main.py
setup.py
app/ ->
__init__.py
package_a/ ->
__init__.py
module_a.py
package_b/ ->
__init__.py
module_b.py
python main.py
.main.py
hace: import app.package_a.module_a
module_a.py
hace import app.package_b.module_b
Alternativamente, 2 o 3 podrían usar: from app.package_a import module_a
Eso funcionará mientras tengas app
en tu PYTHONPATH. main.py
podría estar en cualquier lugar entonces.
Entonces, escribe un setup.py
para copiar (instalar) todo el paquete de aplicación y los subpaquetes en las carpetas python del sistema de destino, y main.py
en las carpetas de script del sistema de destino.
Aquí está la solución que funciona para mí:
Realizo las importaciones relativas como from ..sub2 import mod2
y luego, si quiero ejecutar mod1.py
, voy al directorio principal de app
y ejecuto el módulo usando el conmutador python -m como python -m app.sub1.mod1
.
La verdadera razón por la que este problema ocurre con las importaciones relativas es que las importaciones relativas funcionan tomando la propiedad __name__
del módulo. Si el módulo se está ejecutando directamente, __name__
se establece en __main__
y no contiene ninguna información sobre la estructura del paquete. Y, es por eso que Python se queja sobre el error relative import in non-package
.
Por lo tanto, al usar el modificador -m, proporciona la información de la estructura del paquete a python, mediante la cual puede resolver las importaciones relativas con éxito.
He encontrado este problema muchas veces mientras hago importaciones relativas. Y, después de leer todas las respuestas anteriores, todavía no pude averiguar cómo resolverlo de una manera limpia, sin necesidad de poner código repetitivo en todos los archivos. (Aunque algunos de los comentarios fueron realmente útiles, gracias a @ncoghlan y @XiongChiamiov)
Espero que esto ayude a alguien que está luchando con un problema de importaciones relativas, porque pasar por PEP realmente no es divertido.
"Guido ve ejecutando scripts dentro de un paquete como un anti-patrón" (rechazado PEP-3122 )
He pasado mucho tiempo tratando de encontrar una solución, leyendo publicaciones relacionadas aquí en Stack Overflow y diciéndome a mí mismo "¡debe haber una mejor manera!". Parece que no hay.
Esto se soluciona al 100%:
Importar configuración/local_setting.py en app/main.py:
main.py:
import sys
sys.path.insert(0, "../settings")
try:
from local_settings import *
except ImportError:
print('No Import')
def import_path(fullpath):
"""
Import a file with full path specification. Allows one to
import from anywhere, something __import__ does not do.
"""
path, filename = os.path.split(fullpath)
filename, ext = os.path.splitext(filename)
sys.path.append(path)
module = __import__(filename)
reload(module) # Might be out of date
del sys.path[-1]
return module
Estoy usando este fragmento para importar módulos desde rutas, espero que ayude
explicación de nosklo's
responder con ejemplos
nota: todos los archivos __init__.py
están vacíos.
main.py
app/ ->
__init__.py
package_a/ ->
__init__.py
fun_a.py
package_b/ ->
__init__.py
fun_b.py
def print_a():
print 'This is a function in dir package_a'
from app.package_a.fun_a import print_a
def print_b():
print 'This is a function in dir package_b'
print 'going to call a function in dir package_a'
print '-'*30
print_a()
from app.package_b import fun_b
fun_b.print_b()
si ejecuta $ python main.py
devuelve:
This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
from app.package_b import fun_b
from app.package_a.fun_a import print_a
entonces el archivo en la carpeta package_b
usó el archivo en la carpeta package_a
, que es lo que quiere ¿¿Derecha??
Desafortunadamente, este es un hack de sys.path, pero funciona bastante bien.
Encontré este problema con otra capa: ya tenía un módulo con el nombre especificado, pero era el módulo incorrecto.
lo que quería hacer era lo siguiente (el módulo desde el que estaba trabajando era el módulo 3):
mymodule\
__init__.py
mymodule1\
__init__.py
mymodule1_1
mymodule2\
__init__.py
mymodule2_1
import mymodule.mymodule1.mymodule1_1
Tenga en cuenta que ya he instalado mymodule, pero en mi instalación no tengo "mymodule1"
y obtendría un ImportError porque estaba intentando importar desde mis módulos instalados.
Intenté hacer un sys.path.append, y eso no funcionó. Lo que funcionó fue un sys.path.insert
if __== '__main__':
sys.path.insert(0, '../..')
¡Qué clase de piratería, pero que todo funcionó! Entonces, tenga en cuenta que si desea que su decisión sea anular otras rutas, entonces necesita usar sys.path.insert (0, ruta de acceso) para que funcione. Esto fue un punto de fricción muy frustrante para mí, mucha gente dice usar la función "agregar" a sys.path, pero eso no funciona si ya tiene un módulo definido (me parece un comportamiento muy extraño)
Permítanme poner esto aquí para mi propia referencia. Sé que no es un buen código de Python, pero necesitaba un script para un proyecto en el que estaba trabajando y quería colocar el script en un directorio scripts
.
import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
Como @EvgeniSergeev dice en los comentarios al OP, puede importar código desde un archivo .py
en una ubicación arbitraria con:
import imp
foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
Esto se toma de este SO responde .
Mire http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports Podrías hacerlo
from .mod1 import stuff
Desde Python doc ,
En Python 2.5, puedes cambiar el comportamiento de las importaciones a importaciones absolutas usando una directiva
from __future__ import absolute_import
. Este comportamiento de importación absoluta se convertirá en el predeterminado en una versión futura (probablemente Python 2.7). Una vez que las importaciones absolutas son el valor predeterminado,import string
siempre encontrará la versión de la biblioteca estándar. Se sugiere que los usuarios deberían comenzar a utilizar las importaciones absolutas tanto como sea posible, por lo que es preferible comenzar a escribirfrom pkg import string
en su código.
Además de lo que dijo John B, parece que la configuración de la variable __package__
debería ayudar, en lugar de cambiar __main__
que podría arruinar otras cosas. Pero hasta donde pude probar, no funciona completamente como debería.
Tengo el mismo problema y ninguno de los PEP 328 o 366 resuelve el problema por completo, ya que ambos, al final del día, necesitan que el responsable del paquete se incluya en sys.path
, hasta donde pude entender.
También debo mencionar que no encontré cómo dar formato a la cadena que debería incluir esas variables. ¿Es "package_head.subfolder.module_name"
o qué?
Descubrí que es más fácil establecer la variable de entorno "PYTHONPATH" en la carpeta superior:
bash$ export PYTHONPATH=/PATH/TO/APP
entonces:
import sub1.func1
#...more import
por supuesto, PYTHONPATH es "global", pero aún no me causó problemas.