python - clases - Importar un módulo desde una ruta relativa
importar clases en python (23)
¿Cómo importo un módulo Python dada su ruta relativa?
Por ejemplo, si dirFoo
contiene Foo.py
y dirBar
, y dirBar
contiene Bar.py
, ¿cómo puedo importar Bar.py
en Foo.py
?
Aquí hay una representación visual:
dirFoo/
Foo.py
dirBar/
Bar.py
Foo
desea incluir Bar
, pero la reestructuración de la jerarquía de carpetas no es una opción.
La forma rápida y sucia para los usuarios de Linux
Si solo está haciendo pequeños retoques y no le importan los problemas de implementación, puede usar un enlace simbólico (suponiendo que su sistema de archivos lo admita) para que el módulo o paquete sea directamente visible en la carpeta del módulo solicitante.
ln -s (path)/module_name.py
o
ln -s (path)/package_name
Nota: Un "módulo" es cualquier archivo con la extensión .py y un "paquete" es cualquier carpeta que contenga el archivo __init__.py
(que puede ser un archivo vacío). Desde el punto de vista de uso, los módulos y los paquetes son idénticos: ambos exponen sus "definiciones y declaraciones" contenidas según se solicita mediante el comando de import
.
Agrega un archivo __init__.py :
dirFoo/
Foo.py
dirBar/
__init__.py
Bar.py
Luego agrega este código al inicio de Foo.py:
import sys
sys.path.append(''dirBar'')
import Bar
Aquí hay una manera de importar un archivo desde un nivel superior, usando la ruta relativa.
Básicamente, solo mueva el directorio de trabajo un nivel (o cualquier ubicación relativa), agréguelo a su ruta y luego mueva el directorio de trabajo a donde comenzó.
#to import from one level above:
cwd = os.getcwd()
os.chdir("..")
below_path = os.getcwd()
sys.path.append(below_path)
os.chdir(cwd)
Asegúrese de que dirBar tenga el archivo __init__.py
; esto convierte un directorio en un paquete de Python.
Asumiendo que ambos directorios son paquetes reales de Python (tiene el archivo __init__.py
dentro de ellos), aquí hay una solución segura para la inclusión de módulos en relación con la ubicación del script.
Supongo que desea hacer esto, porque necesita incluir un conjunto de módulos con su script. Utilizo esto en producción en varios productos y funciona en muchos escenarios especiales como: scripts llamados desde otro directorio o ejecutados con Python se ejecutan en lugar de abrir un nuevo intérprete.
import os, sys, inspect
# realpath() will make your script run, even if you symlink it :)
cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]))
if cmd_folder not in sys.path:
sys.path.insert(0, cmd_folder)
# Use this if you want to include modules from a subfolder
cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"subfolder")))
if cmd_subfolder not in sys.path:
sys.path.insert(0, cmd_subfolder)
# Info:
# cmd_folder = os.path.dirname(os.path.abspath(__file__)) # DO NOT USE __file__ !!!
# __file__ fails if the script is called in different ways on Windows.
# __file__ fails if someone does os.chdir() before.
# sys.argv[0] also fails, because it doesn''t not always contains the path.
Como beneficio adicional, este enfoque le permite forzar a Python a usar su módulo en lugar de los instalados en el sistema.
¡Advertencia! Realmente no sé qué sucede cuando el módulo actual está dentro de un archivo de egg
. Probablemente también falla.
Bueno, como mencionó, por lo general, desea tener acceso a una carpeta con sus módulos en relación con el lugar donde se ejecuta el script principal, por lo que simplemente los importa.
Solución:
Tengo el script en D:/Books/MyBooks.py
y algunos módulos (como oldies.py). Necesito importar desde el subdirectorio D:/Books/includes
:
import sys,site
site.addsitedir(sys.path[0] + ''//includes'')
print (sys.path) # Just verify it is there
import oldies
Coloque una print(''done'')
en oldies.py
, para que verifique que todo va bien. Esta forma siempre funciona porque, según la definición de sys.path
Python que se inicializó al iniciar el programa, el primer elemento de esta lista, path[0]
, es el directorio que contiene el script que se usó para invocar al intérprete de Python.
Si el directorio del script no está disponible (por ejemplo, si el intérprete se invoca interactivamente o si el script se lee desde la entrada estándar), la path[0]
es la cadena vacía, que dirige a Python a buscar módulos en el directorio actual primero. Observe que el directorio de secuencias de comandos se inserta antes de las entradas insertadas como resultado de PYTHONPATH
.
Ejemplo relativo de sys.path:
# /lib/my_module.py
# /src/test.py
if __name__ == ''__main__'' and __package__ is None:
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ''../lib'')))
import my_module
Basado en this respuesta.
El método más sencillo es usar sys.path.append ().
Sin embargo, también puede estar interesado en el módulo imp . Proporciona acceso a funciones de importación internas.
# mod_name is the filename without the .py/.pyc extention
py_mod = imp.load_source(mod_name,filename_path) # Loads .py file
py_mod = imp.load_compiled(mod_name,filename_path) # Loads .pyc file
Esto se puede usar para cargar módulos dinámicamente cuando no conoce el nombre de un módulo.
He usado esto en el pasado para crear una interfaz de tipo plugin para una aplicación, donde el usuario escribiría un script con funciones específicas de la aplicación, y simplemente colocaría su script en un directorio específico.
Además, estas funciones pueden ser útiles:
imp.find_module(name[, path])
imp.load_module(name, file, pathname, description)
En mi opinión, la mejor opción es poner __ init __.py en la carpeta y llamar al archivo con
from dirBar.Bar import *
No se recomienda usar sys.path.append () porque algo puede salir mal si usa el mismo nombre de archivo que el paquete de Python existente. No he probado eso pero será ambiguo.
Este es el PEP relevante:
http://www.python.org/dev/peps/pep-0328/
En particular, presumir dirFoo es un directorio desde dirBar ...
En dirFoo / Foo.py:
from ..dirBar import Bar
Esto también funciona, y es mucho más simple que cualquier cosa con el módulo sys
:
with open("C:/yourpath/foobar.py") as f:
eval(f.read())
La forma más sencilla sin ninguna modificación en su script es establecer la variable de entorno PYTHONPATH. Porque sys.path se inicializa desde estas ubicaciones:
- El directorio que contiene el script de entrada (o el directorio actual).
- PYTHONPATH (una lista de nombres de directorio, con la misma sintaxis que la variable de shell PATH).
- El predeterminado dependiente de la instalación.
Solo corre:
export PYTHONPATH=/absolute/path/to/your/module
Tu sys.path contendrá la ruta anterior, como se muestra a continuación:
print sys.path
['''', ''/absolute/path/to/your/module'', ''/usr/lib/python2.7'', ''/usr/lib/python2.7/plat-linux2'', ''/usr/lib/python2.7/lib-tk'', ''/usr/lib/python2.7/lib-old'', ''/usr/lib/python2.7/lib-dynload'', ''/usr/local/lib/python2.7/dist-packages'', ''/usr/lib/python2.7/dist-packages'', ''/usr/lib/python2.7/dist-packages/PIL'', ''/usr/lib/python2.7/dist-packages/gst-0.10'', ''/usr/lib/python2.7/dist-packages/gtk-2.0'', ''/usr/lib/pymodules/python2.7'', ''/usr/lib/python2.7/dist-packages/ubuntu-sso-client'', ''/usr/lib/python2.7/dist-packages/ubuntuone-client'', ''/usr/lib/python2.7/dist-packages/ubuntuone-control-panel'', ''/usr/lib/python2.7/dist-packages/ubuntuone-couch'', ''/usr/lib/python2.7/dist-packages/ubuntuone-installer'', ''/usr/lib/python2.7/dist-packages/ubuntuone-storage-protocol'']
Llámeme demasiado cauteloso, pero me gusta hacer que el mío sea más portátil porque no es seguro asumir que los archivos siempre estarán en el mismo lugar en cada computadora. Personalmente tengo el código buscar primero la ruta del archivo. Yo uso Linux para que el mío se vea así:
import os, sys
from subprocess import Popen, PIPE
try:
path = Popen("find / -name ''file'' -type f", shell=True, stdout=PIPE).stdout.read().splitlines()[0]
if not sys.path.__contains__(path):
sys.path.append(path)
except IndexError:
raise RuntimeError("You must have FILE to run this program!")
Eso es, por supuesto, a menos que planee empaquetar estos juntos. Pero si ese es el caso, en realidad no necesitas dos archivos separados de todos modos.
Mira el módulo pkgutil de la biblioteca estándar. Puede ayudarte a hacer lo que quieras.
No tengo experiencia con Python, así que si hay algún error en mis palabras, solo dímelo. Si su jerarquía de archivos organizada de esta manera:
project/
module_1.py
module_2.py
module_1.py
define una función llamada func_1()
, module_2.py :
from module_1 import func_1
def func_2():
func_1()
if __name__ == ''__main__'':
func_2()
y ejecuta python module_2.py
en cmd, ejecutará lo que func_1()
. Así es como usualmente importamos los mismos archivos de jerarquía. Pero cuando escribe from .module_1 import func_1
en module_2.py
, el intérprete de Python dirá No module named ''__main__.module_1''; ''__main__'' is not a package
No module named ''__main__.module_1''; ''__main__'' is not a package
. Entonces, para solucionar este problema, solo conservamos el cambio que acabamos de realizar, movemos ambos módulos a un paquete y module_2.py
un tercer módulo como interlocutor para ejecutar module_2.py
.
project/
package_1/
module_1.py
module_2.py
main.py
main.py :
from package_1.module_2 import func_2
def func_3():
func_2()
if __name__ == ''__main__'':
func_3()
Pero la razón por la que añadimos a .
antes de module_1
en module_2.py
es que si no hacemos eso y ejecutamos main.py
, el intérprete de python dirá No module named ''module_1''
, eso es un poco complicado, module_1.py
está justo al lado de module_2.py
. Ahora dejo que func_1()
en module_1.py
haga algo:
def func_1():
print(__name__)
que __name__
registra quién llama func_1. Ahora mantenemos el .
antes de module_1
, ejecute main.py
, se imprimirá package_1.module_1
, no module_1
. Indica que el que llama a func_1()
está en la misma jerarquía que main.py
, el .
implica que module_1
está en la misma jerarquía que module_2.py
sí. Entonces, si no hay un punto, main.py
reconocerá el module_1
en la misma jerarquía que él, puede reconocer el package_1
, pero no lo que está "debajo" de él.
Ahora vamos a hacerlo un poco complicado. Tiene un config.ini
y un módulo define una función para leerlo en la misma jerarquía que ''main.py''.
project/
package_1/
module_1.py
module_2.py
config.py
config.ini
main.py
Y por alguna razón inevitable, debe llamarlo con module_2.py
, por lo que tiene que importar desde la jerarquía superior. módulo_2.py :
import ..config
pass
Dos puntos significa importar desde la jerarquía superior (tres puntos acceden arriba que arriba, y así sucesivamente). Ahora ejecutamos main.py
, el intérprete dirá: ValueError:attempted relative import beyond top-level package
. El "paquete de nivel superior" aquí es main.py
Simplemente porque config.py
está al lado de main.py
, están en la misma jerarquía, config.py
no está "debajo de" main.py
, o no está "liderado" por main.py
, por lo que está más allá de main.py
. Para solucionar esto, la forma más sencilla es:
project/
package_1/
module_1.py
module_2.py
config.py
config.ini
main.py
Creo que coincide con el principio de organizar la jerarquía de archivos del proyecto, debe organizar los módulos con diferentes funciones en diferentes carpetas, y solo dejar a la persona que llama en el exterior, y puede importar lo que quiera.
Otra solución sería instalar el paquete py-require y luego usar lo siguiente en Foo.py
import require
Bar = require(''./dirBar/Bar'')
Para este caso, para importar Bar.py en Foo.py, primero convertiría estas carpetas en paquetes de Python así:
dirFoo/
__init__.py
Foo.py
dirBar/
__init__.py
Bar.py
Entonces lo haría así en Foo.py:
from .dirBar import Bar
Si quisiera que el espacio de nombres se pareciera a Bar. lo que sea , o
from . import dirBar
Si quisiera el espacio de nombres dirBar.Bar. lo que sea Este segundo caso es útil si tiene más módulos en el paquete dirBar.
Si estructuras tu proyecto de esta manera:
src/
__init__.py
main.py
dirFoo/
__init__.py
Foo.py
dirBar/
__init__.py
Bar.py
Entonces desde Foo.py deberías poder hacer:
import dirFoo.Foo
O:
from dirFoo.Foo import FooObject
Según el comentario de Tom, esto requiere que se site_packages
acceder a la carpeta src
través de site_packages
o su ruta de búsqueda. Además, como él menciona, __init__.py
se importa implícitamente la primera vez que importa un módulo en ese paquete / directorio. Normalmente, __init__.py
es simplemente un archivo vacío.
Simplemente haga cosas simples para importar el archivo .py desde una carpeta diferente.
Digamos que tienes un directorio como:
lib/abc.py
A continuación, simplemente mantenga un archivo vacío en la carpeta lib como se llama
__init__.py
Y luego usar
from lib.abc import <Your Module name>
Mantenga el archivo __init__.py
en cada carpeta de la jerarquía del módulo de importación.
Simplemente puedes usar: from Desktop.filename import something
Ejemplo:
test.py
que el archivo tiene el nombretest.py
en el directorioUsers/user/Desktop
, se importará todo.
el código:
from Desktop.test import *
Pero asegúrese de hacer un archivo vacío llamado " __init__.py
" en ese directorio
También puede agregar el subdirectorio a su ruta de Python para que se importe como un script normal.
import sys
sys.path.insert(0, <path to dirFoo>)
import Bar
from .dirBar import Bar
en lugar de:
from dirBar import Bar
por si acaso pudiera haber otro dirBar instalado y confundir un lector foo.py.
import os
import sys
lib_path = os.path.abspath(os.path.join(__file__, ''..'', ''..'', ''..'', ''lib''))
sys.path.append(lib_path)
import mymodule