it-swarm-es.tech

¿Cómo hacer importaciones relativas en Python?

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)

483
Joril

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.

303
John B
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. Usted ejecuta python main.py.
  2. main.py hace: import app.package_a.module_a
  3. 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.

114
nosklo

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.

113
Pankaj

"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.

46
lesnik

Esto se soluciona al 100%:

  • app /
    • main.py
  • configuraciones/icono de la imagen.
    • local_setings.py

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

24
iElectric

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

app/package_a/fun_a.py

def print_a():
    print 'This is a function in dir package_a'

app/package_b/fun_b.py

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

main.py

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
  • main.py hace: from app.package_b import fun_b
  • fun_b.py hace 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??

21
suhailvs

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)

11
Garrett Berg

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__), "..")))
10
milkypostman

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 .

8
LondonRob
4
mossplix

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 escribir from pkg import string en su código.

2
jung rhew

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é?

1
Gabriel

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.

1
Andrew_1510