python - tutorial - web applications with django




La respuesta definitiva a las importaciones relativas de Python (2)

Sé que hay muchas preguntas sobre los mismos problemas de importación en Python, pero parece que nadie logró dar un ejemplo claro del uso correcto.

Digamos que tenemos un paquete mypackage con dos módulos foo y bar . Dentro de foo tenemos que poder acceder a la bar .

Como todavía estamos desarrollándolo, mypackage no está en sys.path .

Queremos ser capaces de:

  • importar mypackage.foo
  • ejecute foo.py como un script y ejecute el uso de muestra o las pruebas de la sección __main__ .
  • usa Python 2.5

¿Cómo tenemos que hacer la importación en foo.py para estar seguros de que funcionará en todos estos casos?

# mypackage/__init__.py
...

# mypackage/foo/__init__.py
...

# mypackage/bar.py  
def doBar()
    print("doBar")

# mypackage/foo/foo.py
import bar # fails with module not found
import .bar #fails due to ValueError: Attempted relative import in non-package

def doFoo():
    print(doBar())

if __name__ == '__main__':
    doFoo()

Eche un vistazo a la siguiente información de PEP 328 :

Las importaciones relativas usan el atributo __name__ un módulo para determinar la posición de ese módulo en la jerarquía del paquete. Si el nombre del módulo no contiene ninguna información del paquete (p. Ej., Está establecido en '__main__' ), las importaciones relativas se resuelven como si el módulo fuera un módulo de nivel superior, independientemente de dónde esté realmente ubicado el módulo en el sistema de archivos.

Cuando ejecuta foo.py como script, el __name__ ese módulo es '__main__' , por lo que no puede hacer importaciones relativas. Esto sería cierto incluso si mi mypackage estaba en sys.path . Básicamente, solo puede hacer importaciones relativas desde un módulo si ese módulo se importó.

Aquí hay algunas opciones para solucionar esto:

1) En foo.py , compruebe si __name__ == '__main__' y agregue condicionalmente mypackage a sys.path :

if __name__ == '__main__':
    import os, sys
    # get an absolute path to the directory that contains mypackage
    foo_dir = os.path.dirname(os.path.join(os.getcwd(), __file__))
    sys.path.append(os.path.normpath(os.path.join(foo_dir, '..', '..')))
    from mypackage import bar
else:
    from .. import bar

2) from mypackage import bar siempre la bar from mypackage import bar y ejecute foo.py de tal forma que mypackage sea ​​visible automáticamente:

$ cd <path containing mypackage>
$ python -m mypackage.foo.foo

Mi solución parece un poco más limpia y puede ir en la parte superior, con todas las demás importaciones:

try:
   from foo import FooClass
except ModuleNotFoundError:
   from .foo import FooClass






python-2.5