2010-09-01 12 views
26

Uso de las importaciones relativas en Python tiene un inconveniente, que no será capaz de ejecutar los módulos como Standalones más porque obtendrá una excepción: ValueError: Attempted relative import in non-package¿Cómo usar adecuadamente las importaciones relativas o absolutas en los módulos de Python?

# /test.py: just a sample file importing foo module 
import foo 
... 

# /foo/foo.py: 
from . import bar 
... 
if __name__ == "__main__": 
    pass 

# /foo/bar.py: a submodule of foo, used by foo.py 
from . import foo 
... 
if __name__ == "__main__": 
    pass 

Cómo debería modificar el código de la muestra con el fin de capaz de ejecutar todo: test.py, foo.py y bar.py

Estoy buscando una solución que funcione con Python 2.6+ (incluyendo 3.x).

+1

Compruebe este hilo a cabo: http://www.velocityreviews.com/forums/t502905-relative-import-broken.html –

+0

Gracias, lamentablemente, estaba al tanto de este hilo antiguo, pero no encontré solución al problema. Hasta ahora solo observé a mucha gente quejándose de esto. Necesitamos una solución/ejemplo claro para este problema. – sorin

+0

Relacionado: [¿Cómo saber si el script de Python se ejecutó utilizando la opción -m del intérprete?] (Http://stackoverflow.com/questions/8348726/) –

Respuesta

16

En primer lugar, supongo que se da cuenta de que lo que ha escrito daría lugar a un problema de importación circular, porque foo importa la barra y viceversa; intente agregar

from foo import bar 

a test.py, y verá que falla. El ejemplo debe ser cambiado para poder funcionar.

Entonces, lo que está pidiendo es realmente retroceder a la importación absoluta cuando falla la importación relativa; de hecho, si está ejecutando foo.py o bar.py como el módulo principal, los demás módulos se encontrarán en el nivel raíz, y si comparten el nombre con otro módulo del sistema, el que se seleccionará depende de el orden en sys.path. Como el directorio actual suele ser el primero, los módulos locales se seleccionarán si están disponibles; es decir, si tiene un archivo 'os.py' en el directorio de trabajo actual, se seleccionará en lugar de uno integrado.

Una sugerencia es possibile:

foo.py

try: 
    from . import bar 
except ValueError: 
    import bar 

if __name__ == "__main__": 
    pass 

bar.py:

if __name__ == "__main__": 
    pass 

Por cierto llamar scripts desde la posición adecuada suele ser manera mejor.

python -m foo.bar 

Probablemente es la mejor manera de hacerlo. Esto runs the module as a script.

+0

No creo que la 'barra de importación' en la declaración 'excepto' funcionará para Python 3.1+ – John

+0

@John por lo que puedo ver, no hay razón porque la importación en la excepción no debería funcionar. Funciona al 100% en Python 3.2. –

-2

Hasta ahora, la única solución que encontré fue no utilizar importaciones relativas en absoluto.

Debido a la limitación actual, me pregunto cuándo se supone que alguien debe usar importaciones relativas en python.

En todas las configuraciones que utilicé el sys.path contenía el directorio actual como primer argumento, así que simplemente use import foo en lugar de from . import foo porque hará lo mismo.

+3

Pero esto causará problemas cuando decida que desea utilizar un módulo python estándar o de terceros con el mismo nombre que un módulo en su paquete. Es por eso que en mi humilde opinión, es mucho mejor usar 'de __future__ import absolute_import' e importaciones relativas. –

22

Se podía empezar 'para ejecutar los módulos como Standalones' en un poco de una manera diferente:

En lugar de:

python foo/bar.py 

Uso:

python -mfoo.bar 

Por supuesto, el foo/__init__.py archivo debe estar presente.

Tenga en cuenta que tiene una dependencia circular entre foo.py y bar.py - esto no funcionará. Supongo que es solo un error en tu ejemplo.

Actualización: parece que también funciona perfectamente bien utilizar esto como la primera línea de la foo/bar.py:

#!/usr/bin/python -mfoo.bar 

A continuación, puede ejecutar el script directamente en los sistemas POSIX.

+3

Esto es un hack feo que considero inaceptable, especialmente porque hace mucho más difícil ejecutarlos (especialmente en Windows) – sorin

+0

Por cierto, el ejemplo de bogdan funcionó, incluso con la importación circular. – sorin

+15

+1, esta es exactamente la manera correcta de "ejecutar autónomamente" un módulo ** como parte de un paquete ** (que debe, si desea usar importaciones relativas, por supuesto). –

0

¿Por qué no simplemente poner el "principal" en un archivo .py diferente?

1

Importaciones relativas de zanja: en cualquier caso, debería pensar en el espacio de nombres de su paquete como global.

El truco para hacer esto apetecible es editar sys.path adecuadamente. Aquí hay algo de reflexión:

 
# one directory up 
_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 
sys.path.insert(0, _root_dir)for now 
+4

-1. No abandone las importaciones relativas; se consideraron correctos cuando la característica de importación absoluta se diseñó en http://mail.python.org/pipermail/python-dev/2003-December/041065.html y hoy están igual de bien. – bignose

1

Necesita __init__.py en cada carpeta.

importación relativa sólo funciona cuando lo hace:

python test.py 

importaciones test.py foo.py y foo.py puede importar cualquier cosa relativa de la carpeta de test.py y superiores.

No se puede hacer:

cd foo 
python foo.py 
python bar.py 

nunca funcionará.

Puede probar la solución sys.path.append o sys.path.insert pero arruinará las rutas y tendrá problemas con f = abrir (nombre de archivo).

Cuestiones relacionadas