Importar un módulo desde una ruta relativa

759

¿Cómo importo un módulo de Python dada su ruta relativa?

Por ejemplo, si dirFoocontiene Foo.pyy dirBar, y dirBarcontiene Bar.py, ¿Cómo se importan Bar.pyen Foo.py?

Aquí hay una representación visual:

dirFoo\
    Foo.py
    dirBar\
        Bar.py

Foodesea incluir Bar, pero la reestructuración de la jerarquía de carpetas no es una opción.

Jude Allred
fuente
2
Parece stackoverflow.com/questions/72852/… , tal vez?
Joril
3
Comprueba mi respuesta, es la más completa hasta ahora, otras no funcionan en casos especiales, por ejemplo cuando llamas al script desde otro directorio o desde otro script de Python. Ver stackoverflow.com/questions/279237/…
sorin
¡Tuve un problema similar y encontré esto y funciona! apt-get install python-profiler
ldcl289
55
En caso de que alguien quiera hacerlo estáticamente y llegue aquí (como yo lo hice :) también puede configurar la variable de entorno PYTHONPATH
okaram
Mejor es seguir las instrucciones en Lib / site.py para cada caso
Avenida Gez

Respuestas:

333

Suponiendo que ambos directorios son paquetes reales de Python (sí tienen el __init__.pyarchivo dentro de ellos), aquí hay una solución segura para incluir módulos en relación con la ubicación del script.

Supongo que quiere hacer esto, porque necesita incluir un conjunto de módulos con su script. Lo uso en producción en varios productos y funciona en muchos escenarios especiales como: scripts llamados desde otro directorio o ejecutados con Python execute 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 eggarchivo. Probablemente también falla.

Sorin
fuente
55
¿Puedo obtener una explicación de cómo funciona esto? Tengo un problema similar y me encantaría para forzar un DIR módulo de Python en lugar de ejecutar una búsqueda
Carl Crott
Al ejecutar Win 7 Pro 64x y Python 2.7, recibo algunos errores. 1) Tuve que agregar inspeccionar a la lista de importación. 2) El primer valor, [0], en la tupla es una cadena vacía. El segundo, [1], muestra el nombre del archivo. Supongo que el primero debería ser el camino ... ¿Alguna idea?
Adam Lewis el
2
Si usted va para una subcarpeta, utilizar de esta manera: os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]) + "/subfolder")Do NO añadir la subcarpeta antes de abspathque esto provoca fallos graves.
Maximilian Hils
44
@ scr4ve deberías usar os.path.join () en su lugar, y puedes agregar el caso ( cmd_subfolder) directamente a mi respuesta. ¡Gracias!
sorin
77
para mí realpathya genera caminos absolutos, por lo tanto no los necesito abspath. También os.path.dirnamese puede usar en lugar de dividir, haciendo que la indexación quede [0]obsoleta. La línea sería:os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
ted
328

Asegúrese de que dirBar tenga el __init__.pyarchivo; esto convierte un directorio en un paquete de Python.

S.Lott
fuente
204204
Tenga en cuenta que este archivo puede estar completamente vacío.
Harley Holcombe
47
Si el directorio padre de dirBar no se encuentra, sys.pathentonces la presencia de __init__.pyen el dirBardirectorio no ayuda mucho.
jfs
77
-1, agregar __init.py__solo funcionará cuando el directorio ya esté en sys.path y en mi caso no lo estaba. La solución de "sorin" (aceptada) siempre funciona.
Czarek Tomczak
12
"cuando el directorio ya está en sys.path". Si bien es totalmente cierto, ¿cómo podríamos adivinar que el directorio no estaba en sys.pathla pregunta? ¿Quizás hubo algo omitido que no vimos ni supimos?
S.Lott
44
Esto NO significa la respuesta a la pregunta: si el archivo de inicialización está o no allí, esto no hace que Python busque en los subdirectorios. ¿Cómo consiguió cientos de votos a favor?
Gented
261

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
Andrew Cox
fuente
66
Parece que su respuesta no funciona con rutas relativas, consulte stackoverflow.com/questions/279237/…
bogdan
24
Esto hace el trabajo con rutas relativas - sólo tiene que entender que una ruta relativa dependerá de qué directorio se ejecuta desde, lo que hace de esto una mala solución para otra cosa que un corte rápido.
nobar
12
Puedes hacer algo comosys.path.append(os.path.dirname(__file__) + "/relative/path/to/module")
Falko el
osys.path.append(__name__ if __name__ != '__main__' else __loader__.fullname)
vim
1
Considere usar, sys.path.insert(0, <path to dirFoo>)ya que cargará este módulo antes que los módulos del mismo nombre almacenados en otro lugar.
Anton Tarasenko
117
import os
import sys
lib_path = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'lib'))
sys.path.append(lib_path)

import mymodule
lefakir
fuente
2
Me gusta esto porque tienes la flexibilidad de subir un directorio.
Charles L.
21
Debería usar en os.path.join()lugar de unirse por '/', que se romperá en las ventanas (cojo).
0xc0de
18
Esto no es confiable. Depende de cuál sea el directorio de trabajo actual, no del directorio donde vive el script.
jamesdlin
Es posible que desee agregar solo si la ruta aún no está en la ruta. lib_path = os.path.abspath ('../ funciones') si lib_path no está en sys.path: sys.path.append (lib_path)
Brent
en respuesta a @jamesdlin combinando algunas respuestas: ¿qué pasa os.path.abspath(os.path.join(__file__,'..','lib'))?
Matthew Davis el
107

Simplemente haga cosas simples para importar el archivo .py desde una carpeta diferente.

Digamos que tiene un directorio como:

lib/abc.py

Luego, solo 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 __init__.pyarchivo en cada carpeta de la jerarquía del módulo de importación.

Deepak 'Kaseriya'
fuente
79

Si estructura su proyecto de esta manera:

src\
  __init__.py
  main.py
  dirFoo\
    __init__.py
    Foo.py
  dirBar\
    __init__.py
    Bar.py

Luego, 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 srcpueda acceder a la carpeta a través de site_packagessu ruta de búsqueda. Además, como él menciona, __init__.pyse importa implícitamente cuando se importa por primera vez un módulo en ese paquete / directorio. Típicamente __init__.pyes simplemente un archivo vacío.

Bouvard
fuente
Mencione también que init .py se importa cuando se importa el primer módulo dentro de ese paquete. Además, su ejemplo solo funcionará si src está en los paquetes de sitio (o en la ruta de búsqueda)
Tom Leys
3
Esta es la solución más simple que estaba buscando. Si el archivo a importar está seguro en uno de los subdirectorios, esta solución es una joya.
Deepak GM
He intentado lo mismo y fallo. No se por que. ImportError: Ningún módulo llamado customMath
Ming Li
99
¿Por qué alguien querría importar Foo desde Foo.py? Debería ser de Bar.py, supongo.
bhaskarc
from dirFoo import Foodecir Foo.bla(). Si usar import dirFoo.Foouno debería usar dirFoo.Foo.bla(), bastante feo incluso sin la funda de camello.
Cees Timmerman el
45

El método más fácil es usar sys.path.append ().

Sin embargo, también te puede interesar 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.

Lo he usado 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)
Monkut
fuente
Tenga en cuenta que en la documentación, imp.load_source e imp.load_compiled se enumeran como obsoletas. imp.find_module e imp.load_module se recomiendan en su lugar.
amicitas
@amicitas, ¿Puede usted por favor proporcione cualquier referencia a que (lo necesito, y estoy usando Python 2.6 Soy consciente de lo 2,7 docs decir sobre estos, pero no pudo encontrar ninguna referencia respecto 2.6.)
0xc0de
@ 0xc0de Puede encontrar esa declaración en los documentos del módulo imp para python 2.7.3 y python 2.6.7 . Parece que esas funciones ni siquiera están en los documentos para Python 3.2.
amicitas
1
Para importar archivos fuente como este en python 3.3, vea Importar archivo fuente python abitrario. (Python 3.3+) - Desbordamiento de pila . Gracias amigos por los consejos aquí.
nealmcb
44

Esta es la PEP relevante:

http://www.python.org/dev/peps/pep-0328/

En particular, suponiendo que dirFoo es un directorio desde dirBar ...

En dirFoo \ Foo.py:

from ..dirBar import Bar
Peter Crabtree
fuente
funcionó para mí, solo agregué init .py en cada carpeta y éxito para importar
yussan
23

La forma más fácil sin ninguna modificación en su script es establecer la variable de entorno PYTHONPATH. Debido a que sys.path se inicializa desde estas ubicaciones:

  1. El directorio que contiene el script de entrada (o el directorio actual).
  2. PYTHONPATH (una lista de nombres de directorio, con la misma sintaxis que la variable de shell PATH).
  3. El valor predeterminado dependiente de la instalación.

Solo corre:

export PYTHONPATH=/absolute/path/to/your/module

Su sys.path contiene 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']
James Gan
fuente
13

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 podría salir mal si usa el mismo nombre de archivo que el paquete python existente. No he probado eso, pero será ambiguo.

jhana
fuente
raro que from dirBar.Bar import *funciona, pero no from dirBar.Bar import Bar. ¿Sabes por qué * funciona? ¿Qué pasaría si tuviera varios archivos en dirBar / y quisiera obtener solo algunos de ellos (usando un método como el que ha publicado aquí)?
probador
2
@tester: uso from dirBar import Bar.
nobar
@tester es porque fromindica la fuente, y todo importlo que sigue es qué tomar de esa fuente. from dirBar.Bar import Barsignifica "Desde la fuente, importe la fuente en sí", lo que no tiene sentido. los *medios sin embargo, "me dan todo, desde la fuente"
FuriousFolder
11

La manera rápida y sucia para los usuarios de Linux

Si solo está jugando y no le importan los problemas de implementación, puede usar un enlace simbólico (suponiendo que su sistema de archivos lo admita) para hacer 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 una extensión .py y un "paquete" es cualquier carpeta que contiene el archivo __init__.py(que puede ser un archivo vacío). Desde el punto de vista del uso, los módulos y los paquetes son idénticos: ambos exponen sus "definiciones y declaraciones" contenidas según lo solicitado a través del importcomando.

Ver: http://docs.python.org/2/tutorial/modules.html

sin bar
fuente
10
from .dirBar import Bar

en vez de:

from dirBar import Bar

por si acaso podría haber otro dirBar instalado y confundir a un lector foo.py.

jgomo3
fuente
2
No pude hacer esto en Windows. ¿Está esto en Linux?
Gabriel
1
Funcionará desde un script que importa Foo. es decir: main.py importa dirFoo.Foo. Si está intentando ejecutar Foo.py como un script, fallará. Ver stackoverflow.com/questions/72852/…
jgomo3
9

Para este caso para importar Bar.py en Foo.py, primero convertiría estas carpetas en paquetes de Python de esta manera:

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 bajo el paquete dirBar.

Al Conrad
fuente
7

Agregue un archivo __init__.py :

dirFoo\
    Foo.py
    dirBar\
        __init__.py
        Bar.py

Luego agregue este código al inicio de Foo.py:

import sys
sys.path.append('dirBar')
import Bar
Josh
fuente
55
Si dirBarya es un paquete de Python (por la existencia de dirBar/__init__.py), no hay necesidad de añadir dirBara sys.path, ¿no? La declaración import Barde Foo.pydebería ser suficiente.
Santa
5

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 esta respuesta.

Der_Meister
fuente
5

Bueno, como usted menciona, generalmente desea tener acceso a una carpeta con sus módulos en relación con el lugar donde se ejecuta su script principal, por lo que solo debe importarlos.

Solución:

Tengo el script D:/Books/MyBooks.pyy 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, por lo que comprobar que todo va bien. De esta manera, siempre funciona porque, según la definición de Python sys.pathinicializada al iniciar el programa, el primer elemento de esta lista,path[0] es el directorio que contiene el script que se utilizó para invocar al intérprete de Python.

Si el directorio del script no está disponible (por ejemplo, si el intérprete se invoca de forma interactiva o si el script se lee desde la entrada estándar), path[0]es la cadena vacía, que dirige a Python a buscar primero los módulos en el directorio actual. Observe que el directorio del script se inserta antes de las entradas insertadas como resultado de PYTHONPATH.

Avenida Gez
fuente
1
Tuve que usar una barra diagonal en lugar de dos barras diagonales inversas (es decir site.addsitedir(sys.path[0]+'/includes')) en mi primer programa simple de Python break_time.py: https://github.com/ltfschoen/PythonTest . Uso el sistema: MacOS v10.11.5, Python 2.7.12, IDLE IDE 2.7.12, Tk 8.5.9
Luke Schoen el
4

Simplemente puedes usar: from Desktop.filename import something

Ejemplo:

dado que el archivo es nombre test.pyen el directorio Users/user/Desktop, e importará todo.

el código:

from Desktop.test import *

Pero asegúrese de hacer un archivo vacío llamado " __init__.py" en ese directorio

0x1996
fuente
1
se desaconsejan las importaciones destacadas. ver: stackoverflow.com/questions/2386714/why-is-import-bad
axolotl
Sé que es por eso que en primer lugar, escribió import somethingluego dije para que sea más fácil * , básicamente, no es bueno para la memoria RAM y también si 2 funciones tiene el mismo nombre que lo hará en masa de su código
0x1996
3

Otra solución sería instalar el paquete py-require y luego usar lo siguiente enFoo.py

import require
Bar = require('./dirBar/Bar')
Niklas R
fuente
La URL en realidad parece ser pypi.org/project/require.py , y tenga en cuenta que debe instalarse a través de Pip.
Flash Sheridan
1
@FlashSheridan No, ese es un proyecto diferente. Eliminé py-require porque estaba seguro de que nadie lo estaba usando, aunque no pensé en esta publicación. Si aún necesita una require()función, puede echar un vistazo a mi proyecto Node.py
Niklas R
2

Aquí hay una manera de importar un archivo desde un nivel superior, utilizando la ruta relativa.

Básicamente, simplemente mueva el directorio de trabajo hacia arriba un nivel (o cualquier ubicación relativa), agréguelo a su ruta, luego mueva el directorio de trabajo 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)
Justin Muller
fuente
1
No entiendo tu lógica aquí. es demasiado complicado
transilvlad
0

No tengo experiencia con Python, así que si hay algún error en mis palabras, solo dígame. Si su jerarquía de archivos se organizó así:

project\
    module_1.py 
    module_2.py

module_1.pydefine 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.pyen cmd, ejecutará lo que func_1()define. Así es como importamos los mismos archivos de jerarquía. Pero cuando se escribe from .module_1 import func_1en module_2.py, intérprete de python dirá No module named '__main__.module_1'; '__main__' is not a package. Entonces, para solucionar esto, simplemente conservamos el cambio que acabamos de hacer, y movemos ambos módulos a un paquete, y hacemos que se ejecute un tercer módulo como llamador 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 agregamos un .antes module_1en module_2.pyes que si no hacemos eso y corremos main.py, el intérprete de Python dirá No module named 'module_1', eso es un poco complicado, module_1.pyestá justo al lado module_2.py. Ahora dejo func_1()en module_1.pyalgo Do:

def func_1():
    print(__name__)

que __name__registra quién llama a func_1. Ahora guardamos el .antes module_1, corre main.py, se imprimirá package_1.module_1, no module_1. Indica que quien llama func_1()está en la misma jerarquía que main.py, lo .que implica que module_1está en la misma jerarquía que él module_2.pymismo. Entonces, si no hay un punto, main.pyreconocerá module_1en la misma jerarquía que sí mismo, puede reconocerpackage_1 , pero no qué "debajo" de él.

Ahora hagámoslo un poco complicado. Tiene un config.iniy 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 module_2.py, por lo que debe importar desde la jerarquía superior. module_2.py :

 import ..config
 pass

Dos puntos significa importar desde la jerarquía superior (acceso de tres puntos superior a superior, etc.). Ahora corremos main.py, el intérprete dirá: ValueError:attempted relative import beyond top-level package. El "paquete de nivel superior" aquí es main.py. Solo porque config.pyestá al lado main.py, están en la misma jerarquía, config.pyno están "debajo" main.pyo no están "guiados" main.py, por lo que está más allá main.py. Para solucionar esto, la forma más simple es:

project\
    package_1\
        module_1.py
        module_2.py
    config.py
    config.ini
main.py

Creo que eso 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 simplemente dejar una llamada superior en el exterior, y puede importar lo que quiera.

Findon Fassbender
fuente
-5

Esto también funciona, y es mucho más simple que cualquier cosa con el sysmódulo:

with open("C:/yourpath/foobar.py") as f:
    eval(f.read())
jgilley
fuente
1
Si OP iba ​​a codificar una ruta, de todos modos podrían insertar esa ruta en PYTHONPATH. Creo que el punto es hacerlo de una manera que no codifique una ruta ya que se rompería en cualquier otro lugar.
Mateo
-15

Llámame 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, el código busca 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 agruparlos. Pero si ese es el caso, realmente no necesita dos archivos separados de todos modos.

SuperFamousGuy
fuente
11
Súper ineficiente!
0xc0de
1
Los archivos siempre estarán en el mismo lugar en cada computadora, dada una ruta relativa.
Sagar Hatekar