Importar archivos desde diferentes carpetas

1408

Tengo la siguiente estructura de carpetas.

application/app/folder/file.py

y quiero importar algunas funciones de file.py en otro archivo de Python que reside en

application/app2/some_folder/some_file.py

He intentado

from application.app.folder.file import func_name

y algunos otros intentos, pero hasta ahora no pude importar correctamente. ¿Cómo puedo hacer esto?

Ivan
fuente
¡Leer la documentación oficial me ayudó mucho! docs.python.org/3/reference/…
Kavin Raju S

Respuestas:

1446

Por defecto, no puedes. Al importar un archivo, Python solo busca en el directorio actual, el directorio desde el que se ejecuta el script de punto de entrada y sys.pathque incluye ubicaciones como el directorio de instalación del paquete (en realidad es un poco más complejo que esto, pero esto cubre la mayoría de los casos) .

Sin embargo, puede agregar a la ruta de Python en tiempo de ejecución:

# some_file.py
import sys
# insert at 1, 0 is the script path (or '' in REPL)
sys.path.insert(1, '/path/to/application/app/folder')

import file
Cameron
fuente
356
sys.path.append('/path/to/application/app/folder')está más limpio imo
pseudosudo
387
@pseudosudo: Sí, lo es, pero insertarlo al principio tiene la ventaja de garantizar que la ruta se busque antes que otras (incluso las incorporadas) en caso de conflictos de nombres.
Cameron
8
@kreativitea - sys.pathdevuelve una list, no una deque, y sería tonto para convertir la lista una dequeida y vuelta.
ArtOfWarfare
37
¿Se considera una forma pitónica de administrar archivos .py en carpetas? Me pregunto ... ¿por qué no es compatible por defecto? no tiene sentido mantener todos los archivos .py en un solo directorio ..
Ofir
49
@Ofir: No, esta no es una buena solución pitónica limpia. En general, debe usar paquetes (que se basan en árboles de directorios). Esta respuesta fue específica a la pregunta formulada y, por alguna razón, continúa acumulando un gran número de votos a favor.
Cameron
709

No hay nada malo con:

from application.app.folder.file import func_name

Solo asegúrese de que foldertambién contenga un __init__.py, esto permite que se incluya como un paquete. No estoy seguro de por qué hablan las otras respuestas PYTHONPATH.

joey
fuente
47
Porque esto no cubre los casos en los que PYTHONPATHes necesario modificar . Digamos que tiene dos carpetas en el mismo nivel: Ay B. Atiene un __init.py__. Intenta importar algo desde Bdentro A.
msvalkon
32
¿Qué hay dentro del archivo init.pyo __init__.py?
Lorem Ipsum Dolor
48
@ Xinyang Puede ser un archivo vacío. Su propia existencia le dice a Python que trate el directorio como un paquete.
Jay
99
Por supuesto, esta respuesta asume que applicationya appson paquetes (es decir, que ya tiene __init__.pyen ambos). Como resultado de agregar __init__.pytambién a folder, se application.app.folderconvierte en un (sub) paquete , desde el cual puede acceder al módulo application.app.folder.file , cuyo símbolo func_nameahora se puede importar
Yibo Yang el
30
Lo que sea que intente, esto no funcionará. Quiero importar desde un directorio "hermano", así que uno arriba uno abajo. Todos tienen __ init __. Py's, incluido padre. ¿Es este Python 3 específico?
dasWesen
111

Cuando los módulos están en ubicaciones paralelas, como en la pregunta:

application/app2/some_folder/some_file.py
application/app2/another_folder/another_file.py

Esta taquigrafía hace que un módulo sea visible para el otro:

import sys
sys.path.append('../')
slizb
fuente
24
Como advertencia: esto funciona siempre que el script de importación se ejecute desde su directorio contenedor. De lo contrario, el directorio padre de cualquier otro directorio desde el que se ejecute el script se agregará a la ruta y la importación fallará.
Carl Smith
14
Para evitar eso, podemos obtener el directorio principal del archivosys.path.append(os.path.dirname(os.path.abspath(__file__)))
Rahul
Eso no funcionó para mí: tuve que agregar un nombre de directorio adicional allí para volver a subir al padre, de modo que al correr cli/foo.pydesde la línea de comando pudieseimport cli.bar
RCross
2
@Rahul, su solución no funciona para shells interactivos
towi_parallelism
Si lo ejecuta desde su carpeta raíz (es decir, la carpeta de la aplicación), probablemente esté de acuerdo con sys.path.append('.')importar el módulo utilizando from app2.some_folder.some_file import your_function. Alternativamente, lo que funciona para mí se ejecuta python3 -m app2.another_folder.another_filedesde la carpeta raíz.
adicto
68

Primero importe sys en name-file.py

 import sys

En segundo lugar, agregue la ruta de la carpeta en name-file.py

sys.path.insert(0, '/the/folder/path/name-package/')

Tercero Cree un archivo en blanco llamado __ init __.py en su subdirectorio (esto le dice a Python que es un paquete)

  • nombre-archivo.py
  • paquete de nombres
    • __ init __.py
    • name-module.py

Cuarto, importe el módulo dentro de la carpeta en name-file.py

from name-package import name-module
Alex Montoya
fuente
55
Con la carpeta de nombres justo debajo de name-file.py, esto debería funcionar incluso sin el sys.path.insertcomando. Como tal, la respuesta deja la pregunta, si esta solución funciona incluso cuando la carpeta de nombres se encuentra en una ubicación arbitraria.
Bastian
55

Creo que una forma ad-hoc sería utilizar la variable de entornoPYTHONPATH como se describe en la documentación: Python2 , Python3

# Linux & OSX
export PYTHONPATH=$HOME/dirWithScripts/:$PYTHONPATH

# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%
Ax3l
fuente
Espera, ¿reemplazaría myScripts con el nombre del archivo?
Vladimir Putin
44
no, con la ruta del directorio a su archivo .py
Ax3l
1
Desafortunadamente, si está usando Anaconda, esto no funcionará, ya que debajo del capó ¡PYTHONPATH realmente no se usa internamente!
información
Para ver los cambios (recientes) en anaconda, consulte este SO para conocer los flujos de trabajo y los comentarios sobre soluciones alternativas: stackoverflow.com/questions/17386880/… En términos generales, cree e instale paquetes pequeños en lugar de piratear los directorios de importación.
Ax3l
47

Las respuestas aquí carecen de claridad, esto se prueba en Python 3.6

Con esta estructura de carpetas:

main.py
|
---- myfolder/myfile.py

Donde myfile.pytiene el contenido:

def myfunc():
    print('hello')

La declaración de importación en main.pyes:

from myfolder.myfile import myfunc
myfunc()

y esto imprimirá hola .

danday74
fuente
99
agregar un archivo de configuración init .py (vacío) en mi carpeta funcionó para mí en linux (y)
Vincent
77
@Vincent te refieres __init__.py?
mrgloom
2
@mrgloom efectivamente
Vincent
3
Por alguna razón, agregar __init__.pyno funciona para mí. Estoy usando Py 3.6.5 en Ubuntu 18. Funciona en Pycharm pero no desde la terminal
Crearo Rotar
99
Esto no tiene nada que ver con la pregunta que se hace sobre la importación de archivos desde una rama diferente del árbol de archivos que el directorio de trabajo actual.
Alexander Rossa
45

Su problema es que Python busca en el directorio de Python este archivo y no lo encuentra. Debe especificar que está hablando sobre el directorio en el que se encuentra y no sobre el de Python.

Para hacer esto, cambia esto:

from application.app.folder.file import func_name

a esto:

from .application.app.folder.file import func_name

Al agregar el punto que está diciendo, busque en esta carpeta la carpeta de la aplicación en lugar de buscar en el directorio de Python.

CianB
fuente
2
esto es lo que me arregló
Phi
32

Por lo que sé, agregar un __init__.pyarchivo directamente en la carpeta de las funciones que desea importar hará el trabajo.

Vaibhav Singh
fuente
77
solo si el script que quiere incluir ese otro directorio ya está en sys.path
Ax3l
2
He utilizado sys.path.append(tools_dir)en Windows y no necesito añadir un __init__.py' file in my directory tools_dir`
Herve-Guerin
25

En Python 3.4 y versiones posteriores, puede importar directamente desde un archivo fuente (enlace a la documentación) . Esta no es la solución más simple, pero incluyo esta respuesta para completar.

Aquí hay un ejemplo. Primero, el archivo a importar, llamado foo.py:

def announce():
    print("Imported!")

El código que importa el archivo anterior, inspirado en gran medida por el ejemplo en la documentación:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

foo = module_from_file("foo", "/path/to/foo.py")

if __name__ == "__main__":
    print(foo)
    print(dir(foo))
    foo.announce()

La salida:

<module 'foo' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

Tenga en cuenta que el nombre de la variable, el nombre del módulo y el nombre del archivo no necesitan coincidir. Este código aún funciona:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

baz = module_from_file("bar", "/path/to/foo.py")

if __name__ == "__main__":
    print(baz)
    print(dir(baz))
    baz.announce()

La salida:

<module 'bar' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

La importación programática de módulos se introdujo en Python 3.1 y le brinda más control sobre cómo se importan los módulos. Consulte la documentación para más información.

wecsam
fuente
11
No sé si alguien intentó entender esto, pero creo que es demasiado complicado.
Dan
23

Trabajó para mí en python3 en linux

import sys  
sys.path.append(pathToFolderContainingScripts)  
from scriptName import functionName #scriptName without .py extension  
dsg38
fuente
66
sys.path.append("/home/linux/folder/")- Asegúrese de no utilizar un acceso directo, por ejemplo"~/folder/"
daGo
22

Pruebe las importaciones relativas de Python:

from ...app.folder.file import func_name

Cada punto inicial es otro nivel superior en la jerarquía que comienza con el directorio actual.


¿Problemas? Si esto no funciona para usted, entonces probablemente le estén molestando las numerosas importaciones relativas de gotcha. Lea las respuestas y los comentarios para obtener más detalles: Cómo corregir "Intento de importación relativa en un paquete" incluso con __init__.py

Sugerencia: tener __init__.pyen cada nivel de directorio. Es posible que necesite python -m application.app2.some_folder.some_file(dejando .py) que ejecuta desde el directorio de nivel superior o que tenga ese directorio de nivel superior en su PYTHONPATH. ¡Uf!

Zectbumo
fuente
22

Me enfrenté al mismo desafío, especialmente al importar varios archivos, así es como logré superarlo.

import os, sys

from os.path import dirname, join, abspath
sys.path.insert(0, abspath(join(dirname(__file__), '..')))

from root_folder import file_name
Erick Mwazonga
fuente
2
Su respuesta sería más útil si pudiera explicar lo que hace de manera diferente a una importación ordinaria.
not2qubit
1
Tenía /path/dir1/__init__.py y /path/dir1/mod.py. Para /path/some.py de dir1.mod import func funcionó. Cuando en /path/dir2/some.py solo funcionó después de que copié y pegué la respuesta anterior en la parte superior del archivo. No quería editar mi ruta ya que no todos los proyectos de Python que tengo están en / path /.
jwal
19

Teniendo en cuenta applicationque el directorio raíz para su proyecto pitón, crear un vacío __init__.pyarchivo en application, appy foldercarpetas. Luego, en su some_file.pyhacer cambios de la siguiente manera para obtener la definición de func_name:

import sys
sys.path.insert(0, r'/from/root/directory/application')

from application.app.folder.file import func_name ## You can also use '*' wildcard to import all the functions in file.py file.
func_name()
ChandanK
fuente
debe ser: sys.path.insert (0, r '/ from / root / directory')
Bolaka
18

Usar sys.path.append con una ruta absoluta no es ideal cuando se mueve la aplicación a otros entornos. Usar una ruta relativa no siempre funcionará porque el directorio de trabajo actual depende de cómo se invocó el script.

Como la estructura de carpetas de la aplicación es fija, podemos usar os.path para obtener la ruta completa del módulo que deseamos importar. Por ejemplo, si esta es la estructura:

/home/me/application/app2/some_folder/vanilla.py
/home/me/application/app2/another_folder/mango.py

Y digamos que desea importar el módulo "mango". Puede hacer lo siguiente en vanilla.py:

import sys, os.path
mango_dir = (os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ '/another_folder/')
sys.path.append(mango_dir)
import mango

Por supuesto, no necesita la variable mango_dir.

Para entender cómo funciona esto, mira este ejemplo de sesión interactiva:

>>> import os
>>> mydir = '/home/me/application/app2/some_folder'
>>> newdir = os.path.abspath(os.path.join(mydir, '..'))
>>> newdir
    '/home/me/application/app2'
>>> newdir = os.path.abspath(os.path.join(mydir, '..')) + '/another_folder'
>>> 
>>> newdir
'/home/me/application/app2/another_folder'
>>> 

Y revise la documentación de os.path .

Nagev
fuente
13

Esto funciona para mí en windows

# some_file.py on mainApp/app2 
import sys
sys.path.insert(0, sys.path[0]+'\\app2')

import some_file
Emeeus
fuente
10

Soy bastante especial: ¡uso Python con Windows!

Acabo de completar la información: tanto para Windows como para Linux, tanto la ruta relativa como la absoluta funcionan sys.path(necesito rutas relativas porque uso mis scripts en varias PC y en diferentes directorios principales).

Y cuando se usa Windows tanto \y /se puede usar como separador para los nombres de archivo y, por supuesto, debe duplicarse \en cadenas de Python,
algunos ejemplos válidos:

sys.path.append('c:\\tools\\mydir')
sys.path.append('..\\mytools')
sys.path.append('c:/tools/mydir')
sys.path.append('../mytools')

(nota: creo que /es más conveniente que un \evento si es menos 'nativo de Windows' porque es compatible con Linux y más simple de escribir y copiar en el explorador de Windows)

herve-guerin
fuente
44
os.path.join ('herramientas', 'mydir')
Corey Goldberg
7

Si el propósito de cargar un módulo desde una ruta específica es ayudarlo durante el desarrollo de un módulo personalizado, puede crear un enlace simbólico en la misma carpeta del script de prueba que apunte a la raíz del módulo personalizado. La referencia de este módulo tendrá prioridad sobre cualquier otro módulo instalado con el mismo nombre para cualquier script ejecutado en esa carpeta.

Probé esto en Linux pero debería funcionar en cualquier sistema operativo moderno que admita enlaces simbólicos.

Una ventaja de este enfoque es que puede señalar un módulo que se encuentra en su propia copia de trabajo de la rama SVC local, lo que puede simplificar en gran medida el tiempo del ciclo de desarrollo y reducir los modos de falla de la gestión de diferentes versiones del módulo.

Timothy C. Quinn
fuente
7

En mi caso tenía una clase para importar. Mi archivo se veía así:

# /opt/path/to/code/log_helper.py
class LogHelper:
    # stuff here

En mi archivo principal incluí el código a través de:

import sys
sys.path.append("/opt/path/to/code/")
from log_helper import LogHelper
Schmudu
fuente
1
@ not2qubit sys no se importó en la respuesta.
Walter
7

Me encontré con la misma pregunta varias veces, así que me gustaría compartir mi solución.

Versión de Python: 3.X

La siguiente solución es para alguien que desarrolla su aplicación en Python versión 3.X porque Python 2 no es compatible desde el 1 de enero de 2020 .

Estructura del proyecto

En python 3, no necesita __init__.pyen el subdirectorio de su proyecto debido a los Paquetes de espacio de nombres implícitos . Ver ¿ No se requiere init .py para los paquetes en Python 3.3+?

Project 
├── main.py
├── .gitignore
|
├── a
|   └── file_a.py
|
└── b
    └── file_b.py

Planteamiento del problema

En file_b.py , me gustaría importar una clase Aenfile_a.py la carpeta a.

Soluciones

# 1 Una manera rápida pero sucia

Sin instalar el paquete como si actualmente estuviera desarrollando un nuevo proyecto

Usando try catchpara comprobar si los errores. Ejemplo de código:

import sys
try:
    # The insertion index should be 1 because index 0 is this file
    sys.path.insert(1, '/absolute/path/to/folder/a')  # the type of path is string
    # because the system path already have the absolute path to folder a
    # so it can recognize file_a.py while searching 
    from file_a import A
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

# 2 Instala tu paquete

Una vez que instaló su aplicación (en esta publicación, el tutorial de instalación no está incluido)

Puedes simplemente

try:
    from __future__ import absolute_import
    # now it can reach class A of file_a.py in folder a 
    # by relative import
    from ..a.file_a import A  
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

¡Feliz codificación!

WY Hsu
fuente
para más información sobre importaciones absolutas
WY Hsu
3

Estaba trabajando en un proyecto aque quería que los usuarios instalaran pip install acon la siguiente lista de archivos:

.
├── setup.py
├── MANIFEST.in
└── a
    ├── __init__.py
    ├── a.py
    └── b
        ├── __init__.py
        └── b.py

setup.py

from setuptools import setup

setup (
  name='a',
  version='0.0.1',
  packages=['a'],
  package_data={
    'a': ['b/*'],
  },
)

MANIFEST.in

recursive-include b *.*

a / init .py

from __future__ import absolute_import

from a.a import cats
import a.b

a / a.py

cats = 0

a / b / init .py

from __future__ import absolute_import

from a.b.b import dogs

a / b / b.py

dogs = 1

Instalé el módulo ejecutando lo siguiente desde el directorio con MANIFEST.in:

python setup.py install

Entonces, desde una ubicación totalmente diferente en mi sistema de archivos /moustache/armwrestlepude ejecutar:

import a
dir(a)

Lo que confirmó que a.catsefectivamente era igual a 0 y de a.b.dogshecho igual a 1, según lo previsto.

duhaime
fuente
2

En lugar de solo hacer un import ..., haz esto:

from <MySubFolder> import <MyFile>

MyFile está dentro del MySubFolder.

Galileoomega
fuente
1

Puede actualizar el shell de Python presionando f5, o vaya a Ejecutar-> Ejecutar módulo. De esta manera, no tiene que cambiar el directorio para leer algo del archivo. Python cambiará automáticamente el directorio. Pero si desea trabajar con diferentes archivos de diferentes directorios en Python Shell, puede cambiar el directorio en sys, como dijo Cameron anteriormente.

IOstream
fuente
1

Así que hice clic derecho en mi IDE, agregué uno nuevo foldery me preguntaba por qué no podía importarlo. Más tarde me di cuenta de que tenía que hacer clic derecho y crear un paquete de Python, y no una carpeta clásica del sistema de archivos. O bien, un método post mortem está agregando un __init__.py(lo que hace que Python trate la carpeta del sistema de archivos como un paquete) como se menciona en otras respuestas. Agregue esta respuesta aquí en caso de que alguien haya tomado esta ruta.

mithunpaul
fuente
1

Puede usar importlib para importar módulos donde desea importar un módulo desde una carpeta usando una cadena de esta manera:

import importlib

scriptName = 'Snake'

script = importlib.import_module('Scripts\\.%s' % scriptName)

Este ejemplo tiene un main.py que es el código anterior, luego una carpeta llamada Scripts y luego puede llamar a lo que necesite de esta carpeta cambiando la scriptNamevariable. Luego puede usar scriptpara hacer referencia a este módulo. como si tuviera una función llamada Hello()en el módulo Snake, puede ejecutar esta función al hacerlo:

script.Hello()

He probado esto en Python 3.6

Dextron
fuente
0

He tenido estos problemas varias veces. He venido mucho a esta misma página. En mi último problema tuve que ejecutarlo serverdesde un directorio fijo, pero cada vez que depuraba quería ejecutarlo desde diferentes subdirectorios.

import sys
sys.insert(1, /path) 

hicieron NO trabajo para mí porque en diferentes módulos tuve que leer diferentes * .csv archivos que estaban todos en el mismo directorio.

Al final, lo que funcionó para mí no fue pitónico, supongo, pero:

Utilicé un if __main__ encima del módulo que quería depurar , que se ejecuta desde una ruta diferente a la habitual.

Entonces:

# On top of the module, instead of on the bottom
import os
if __name__ == '__main__':
    os.chdir('/path/for/the/regularly/run/directory')
B Furtado
fuente