python - from - Importar rutas-¿la manera correcta?
python relative import (3)
Sé que hay MUCHAS preguntas similares o similares, pero aún no puedo entender / encontrar la manera correcta de trabajar con los módulos. Python es mi idioma favorito, y me gusta todo lo que hay en él excepto trabajar con importaciones: importaciones recursivas (cuando intentas hacer referencia a un nombre que aún no existe), importar rutas, etc.
Entonces, tengo este tipo de estructura de proyecto:
my_project/
package1/
__init__.py
module1
module2
package2/
__init__.py
module1
module2
Package1
puede usarse como una unidad independiente, pero también se espera que sea importado por el package2
. Lo que estoy haciendo ahora, es que, por ejemplo, en package1.module1
escribo from package1 import module2
, es decir, usando la ruta completa al módulo importado. Hago esto porque si uso el import module2
, esto no funcionará cuando el módulo se importará de otro paquete ( package2
). Tampoco puedo usar from . import module2
from . import module2
: esto no funcionará cuando se ejecute directamente module1
.
Bien, para que from package1 import module2
en package1.module1
funcione en ambos casos (cuando se ejecuta directamente package1.module1
y cuando se importa desde package2
) agrego estas líneas al comienzo de package1.module1
:
import os, sys
currDir = os.path.dirname(os.path.realpath(__file__))
rootDir = os.path.abspath(os.path.join(currDir, ''..''))
if rootDir not in sys.path: # add parent dir to paths
sys.path.append(rootDir)
Para mí esto funciona, pero siento que esto no es pythonic. ¿Estoy haciendo algo mal?
¿Debería, en cambio, ejecutar siempre package1.module1
desde la raíz del proyecto? Si es así, esto hace que sea un inconveniente ejecutarlo desde un IDE; de alguna manera necesito establecer rutas en él.
ACTUALIZACIÓN: Intenté agregar un archivo root.pth
a package1
dir con el contenido de ..
Pero no funcionó, supongo que está destinado a otra cosa.
CONCLUSIONES:
Utilice siempre las importaciones absolutas:
import package1.module1
Agregue un bootstrapper a la carpeta raíz para iniciar algunos de los módulos como una secuencia de comandos independiente. Esto resuelve ejecutar el script desde un IDE y es un enfoque pythonic.
El 4/22/07, Brett Cannon escribió:
Este PEP es para cambiar el
if __name__ == "__main__": ...
idioma paraif __name__ == sys.main: ...
para que al menos tenga la oportunidad de ejecutar el módulo en un paquete que use importaciones relativas.Pasé este PEP más allá de ideas de pitón. Detuvo la discusión allí cuando se propusieron demasiadas ideas nuevas. =) Los he enumerado a todos en la sección de Ideas rechazadas, aunque si se presenta un apoyo abrumador para uno, el PEP puede cambiar a uno de ellos.
Estoy -1 en esto y en cualquier otra propuesta de __main__
maquinaria __main__
. El único caso de uso parece estar ejecutando scripts que viven dentro del directorio de un módulo, lo que siempre he visto como un antipatrón. Para hacerme cambiar de opinión, tendrías que convencerme de que no lo es.
¿Cuál es el punto de entrada para su programa? Por lo general, el punto de entrada para un programa estará en la raíz del proyecto. Dado que está en la raíz, todos los módulos dentro de la raíz serán importables, siempre que haya un archivo __init__.py
en ellos.
Entonces, usando tu ejemplo:
my_project/
main.py
package1/
__init__.py
module1
module2
package2/
__init__.py
module1
module2
main.py
sería el punto de entrada para su programa. Debido a que el archivo que se ejecuta como main se coloca automáticamente en PYTHONPATH, tanto package1
como package2
están disponibles desde la importación de nivel superior.
# in main.py
from package1.module1 import *
from package1.module2 import *
# in package1.module1
import module2
from package2.module1 import *
# in package2.module1 import *
import module2
from package1.module1 import *
Tenga en cuenta que en lo anterior, package1 y package2 dependen uno del otro. Ese nunca debería ser el caso. Pero este es solo un ejemplo de poder importar desde cualquier lugar.
main.py
tampoco tiene que ser nada lujoso. Puede ser muy simple:
# main.py
if __name__ == ''__main__'':
from package1.module1 import SomeClass
SomeClass().start()
El punto que estoy tratando de señalar es que si un módulo necesita ser accesible por otros módulos, ese módulo debería estar disponible como una importación de nivel superior. Un módulo no debe intentar colocarse como una importación de nivel superior (directamente en PYTHONPATH).
Debería ser responsabilidad del proyecto garantizar que todas las importaciones puedan satisfacerse si el módulo se incluye directamente en el proyecto. Hay dos maneras de hacer esto. La primera es creando un archivo bootstrapper como main.py
en la carpeta del proyecto. El otro, es mediante la creación de un archivo que agrega todas las rutas relevantes a PYTHONPATH, que se carga por cualquier punto de entrada que pueda existir.
Por ejemplo:
# setup.py
import sys
def load():
paths = [''/path1/'',''/path2/'',''/path3/'']
for p in path:
sys.path.insert(0, p)
# entrypoint.py
from setup import load
load()
# continue with program
Lo principal para quitar, es que un módulo no debe ponerse en el camino. La ruta debe ser determinada automáticamente por el punto de entrada en el programa, o definida explícitamente por un script de configuración que sepa dónde están todos los módulos relevantes.
Generalmente creo cada paquete como un paquete instalable (es decir, creo un archivo setup.py), y luego los instalo en un virtualenv solo para este proyecto, usando pip.
Incluso puede instalar el uso de pip -e si todavía están en desarrollo.
export PYTHONPATH=/path1:/path2:
5 años tarde aquí ... pero solo export PYTHONPATH=/path1:/path2:
(observe el export PYTHONPATH=/path1:/path2:
":"): de esta manera, su directorio de trabajo (desde el que ejecuta python) estará en la ruta.