Importando archivos de diferentes carpetas

1900

Tengo la siguiente estructura de carpetas.

application
├── app
│   └── folder
│       └── file.py
└── app2
    └── some_folder
        └── some_file.py

Quiero importar algunas funciones desde file.pyadentro 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?

4
  • 2
    Relacionado: stackoverflow.com/q/43476403/674039
    wim
    18/04/2017 a las 15:56
  • ¡Leer la documentación oficial me ayudó mucho! docs.python.org/3/reference/… 14 de mayo de 2020 a las 1:20
  • Si tiene un guión en el nombre de la subcarpeta, DEBE ESTAR BAJO PUNTUACIÓN. Por ejemplo my-package y dentro tienes la carpeta my_app y la carpeta de pruebas. Si my_app se llama my-app, tendrás problemas de importación
    Gonzalo
    28 abr a las 15:01
  • Ni applicationtampoco app1, app2, folder, some_folderson los paquetes, y no contienen __init__.py, ¿verdad? Si vas a hacer mucho de esto, es hora de hacer un paquete.
    smci
    14 de junio a las 23:26
1844

Nota: Esta respuesta estaba destinada a una pregunta muy específica. Para la mayoría de los programadores que vienen aquí desde un motor de búsqueda, esta no es la respuesta que está buscando. Normalmente, estructuraría sus archivos en paquetes (consulte otras respuestas) en lugar de modificar la ruta de búsqueda.


De forma predeterminada, no puede. Al importar un archivo, Python solo busca en 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 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
27
  • 457
    sys.path.append('/path/to/application/app/folder') es más limpio en mi opinión 1 de septiembre de 2011 a las 21:48
  • 459
    @pseudosudo: Sí, lo es, pero insertarlo al principio tiene la ventaja de garantizar que la ruta se busque antes que otras (incluso las integradas) en el caso de conflictos de nombres.
    Cameron
    2 de septiembre de 2011 a las 2:47
  • 9
    @kreativitea: sys.pathdevuelve a list, no a deque, y sería una tontería convertir el listen ay dequevolver. 3 de noviembre de 2013 a las 20:35
  • 42
    ¿Se considera una forma pitónica de administrar archivos .py en carpetas? Me pregunto ... ¿por qué no es compatible de forma predeterminada? no tiene sentido mantener todos los archivos .py en un solo directorio ..
    Ofir
    20 de septiembre de 2015 a las 18:32
  • 57
    @Ofir: No, esta no es una buena solución pitónica limpia. En general, debería utilizar paquetes (que se basan en árboles de directorios). Esta respuesta fue específica para la pregunta formulada y, por alguna razón, continúa acumulando un gran número de votos a favor.
    Cameron
    21 de septiembre de 2015 a las 2:38
977

No tiene nada de malo:

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.

26
  • 64
    Porque esto no cubre los casos en los que PYTHONPATHes necesario modificar . Supongamos que tiene dos carpetas en el mismo nivel: Ay B. Atiene un __init.py__. Intente importar algo desde Badentro A. 6 de marzo de 2014 a las 13:45
  • 51
    ¿Qué hay dentro del archivo init.pyor __init__.py?
    Glass
    9 de mayo de 2015 a las 2:16
  • 63
    @Xinyang Puede ser un archivo vacío. Su misma existencia le dice a Python que trate el directorio como un paquete.
    jay
    11 de mayo de 2015 a las 23:24
  • 23
    Actualmente, esta no es la respuesta más votada, pero ES la respuesta más correcta (en la mayoría de los casos). Simplemente cree un paquete. No es dificil. Las otras respuestas son necesarias porque a veces puede estar restringido por ciertos cambios en el sistema (crear o modificar un archivo, etc.) como durante las pruebas. 3 de marzo de 2016 a las 18:59
  • 58
    Lo que sea que intente, esto no funcionará. Quiero importar desde un directorio "hermano", así que uno hacia arriba y hacia abajo. Todos tienen __ init __. Py, incluido el padre. ¿Es este Python 3 específico? 18/06/2017 a las 12:54
156

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 abreviatura hace que un módulo sea visible para el otro:

import sys
sys.path.append('../')
6
  • 31
    Como advertencia: esto funciona siempre que el script de importación se ejecute desde el directorio que lo contiene. De lo contrario, el directorio principal de cualquier otro directorio desde el que se ejecute el script se agregará a la ruta y la importación fallará. 3 de mayo de 2017 a las 3:02
  • 21
    Para evitar eso, podemos obtener el directorio padre del archivo. sys.path.append(os.path.dirname(os.path.abspath(__file__)))
    Rahul
    17 de septiembre de 2018 a las 10:09
  • 1
    Eso no funcionó para mí: tuve que agregar un nombre de directorio adicional allí para volver al padre, de modo que la ejecución cli/foo.pydesde la línea de comando pudieraimport cli.bar
    RCross
    26/07/19 a las 11:23
  • 3
    @Rahul, su solución no funciona para shells interactivos 27/11/19 a las 18:19
  • 3
    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 usando from app2.some_folder.some_file import your_function. Alternativamente, lo que me funciona es ejecutar python3 -m app2.another_folder.another_filedesde la carpeta raíz. 16/12/19 a las 13:23
99

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, haga un archivo en blanco llamado __ init __.py en su subdirectorio (esto le dice a Python que es un paquete)

  • name-file.py
  • paquete de nombre
    • __ init __.py
    • name-module.py

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

from name-package import name-module
2
  • 9
    Con name-folder justo debajo de name-file.py, esto debería funcionar incluso sin el sys.path.insert-comando. 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
    1 de feb. De 2019 a las 9:19
  • ¿estás diciendo que tengo que codificar la ruta al script? Esto significa que la solución no es portátil. También la pregunta es cómo acceder de una subcarpeta a otra. ¿Por qué no seguir la convención de nombres y la estructura de archivos de la pregunta original?
    Giacomo
    13 de marzo a las 18:40
66

Creo que una forma ad-hoc sería usar 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%
4
  • Espera, ¿reemplazaría myScripts con el nombre del archivo? 29/06/2014 a las 22:45
  • 6
    no, con la ruta del directorio a su archivo .py
    Ax3l
    5 de julio de 2014 a las 13:57
  • 3
    Desafortunadamente, si está usando Anaconda, esto no funcionará, ¡ya que, bajo el capó, PYTHONPATH no se usa realmente internamente! 13 abr.20 a las 1:28
  • 1
    Para cambios (recientes) en anaconda, vea este SO para flujos de trabajo y comentarios para soluciones: stackoverflow.com/questions/17386880/… En términos generales, cree e instale paquetes pequeños en lugar de piratear los directorios de importación.
    Ax3l
    14 abr.20 a las 8:53
58

Su problema es que Python está buscando este archivo en el directorio de Python y no lo encuentra. Debe especificar que está hablando del directorio en el que se encuentra y no del 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, está diciendo que busque en esta carpeta la carpeta de la aplicación en lugar de buscar en el directorio de Python.

1
  • ImportError: intento de importación relativa sin un paquete principal conocido :( 26 de agosto a las 8:49
51

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 main.pyes:

from myfolder.myfile import myfunc
myfunc()

y esto imprimirá hola .

11
  • 12
    agregar un archivo de configuración init .py (vacío) en myfolder funcionó para mí en linux (y)
    Vincent
    7 mar. 18 a las 17:45
  • 9
    @Vincent, ¿te refieres __init__.py?
    mrgloom
    27/04/18 a las 12:40
  • 3
    Por alguna razón, agregar __init__.pyno me funciona. Estoy usando Py 3.6.5 en Ubuntu 18. Funciona en Pycharm pero no desde la terminal 15 de septiembre de 2018 a las 18:29
  • 32
    Esto no tiene nada que ver con la pregunta sobre la importación de archivos desde una rama diferente del árbol de archivos que el directorio de trabajo actual. 23 oct 2018 a las 15:38
  • 8
    Preciosos diagramas que ignoran expresamente la pregunta de OP.
    Marc L.
    6/06/19 a las 14:14
38

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

2
  • 7
    solo si el script que quiere incluir ese otro directorio ya está en sys.path
    Ax3l
    20/02/2016 a las 16:53
  • 2
    He utilizado sys.path.append(tools_dir)en Windows y no necesito añadir un __init__.py' file in my directory tools_dir` 18 de marzo de 2017 a las 12:42
37

En Python 3.4 y versiones posteriores, puede importar desde un archivo fuente directamente (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 en el ejemplo de 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 no es necesario que el nombre de la variable, el nombre del módulo y el nombre del archivo coincidan. Este código todavía 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 de módulos mediante programación 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 obtener más información.

2
  • 19
    No sé si alguien intentó siquiera entender esto, pero creo que es demasiado complicado.
    Dan
    7/01/19 a las 22:21
  • 1
    Esta es la única solución que funcionó para mí. Tengo el mismo nombre de archivo en diferentes directorios.
    Sincere
    23 de agosto a las 15:22
28

Prueba 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 se esté molestando por las muchas 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 que no es" incluso con __init__.py

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

2
  • Esto no parece funcionar si el nombre de su directorio comienza con un número (por ejemplo, import ..70_foo.testno está permitido) 17/10/20 a las 11:19
  • 1
    Vaya, esto realmente funcionó. No sabía que se podía "subir" un directorio usando varios puntos. 2 de julio a las 20:34
28

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
2
  • 4
    ¿Su respuesta sería más útil si pudiera explicar lo que hace de manera diferente a una importación ordinaria? 23/04/19 a las 19:25
  • 1
    Tenía /path/dir1/__init__.py y /path/dir1/mod.py. Para /path/some.py from dir1.mod func import func. Cuando estaba 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 / ruta /.
    jwal
    2 de mayo de 2019 a las 11:52
28

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

Dado que la estructura de la carpeta de la aplicación es fija, podemos usarla os.pathpara 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, mire 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 consulte la documentación de os.path .

También vale la pena señalar que el manejo de varias carpetas es más fácil cuando se usan paquetes , ya que se pueden usar nombres de módulos con puntos.

27

Trabajó para mí en python3 en linux

import sys  
sys.path.append(pathToFolderContainingScripts)  
from scriptName import functionName #scriptName without .py extension  
2
  • 8
    sys.path.append("/home/linux/folder/") - Asegúrese de no utilizar un atajo, por ejemplo "~/folder/"
    daGo
    19 de mayo de 2017 a las 12:49
  • 1
    Ésta es la respuesta más sencilla; también funciona para Windows. 16 de junio de 2020 a las 17:05
22

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.pyhaga 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()
1
  • debe ser: sys.path.insert (0, r '/ from / root / directory')
    Bolaka
    13/02/2016 a las 17:51
15

Esto me funciona en Windows

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

import some_file
14

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
1
  • 1
    @ not2qubit sys no se importó en la respuesta.
    Walter
    9/09/19 a las 12:43
13

La mejor práctica para crear un paquete puede ser ejecutar y acceder a los otros módulos desde un módulo como main_module.pyen el directorio de nivel más alto.

Esta estructura demuestra que puede usar y acceder a un subpaquete, paquete principal o paquetes y módulos del mismo nivel mediante el uso de un archivo de directorio de nivel superior main_module.py.

Cree y ejecute estos archivos y carpetas para realizar pruebas:

 package/
    |
    |----- __init__.py (Empty file)
    |------- main_module.py (Contains: import subpackage_1.module_1)        
    |------- module_0.py (Contains: print('module_0 at parent directory, is imported'))
    |           
    |
    |------- subpackage_1/
    |           |
    |           |----- __init__.py (Empty file)
    |           |----- module_1.py (Contains: print('importing other modules from module_1...')
    |           |                             import module_0
    |           |                             import subpackage_2.module_2
    |           |                             import subpackage_1.sub_subpackage_3.module_3)
    |           |----- photo.png
    |           |
    |           |
    |           |----- sub_subpackage_3/
    |                        |
    |                        |----- __init__.py (Empty file)
    |                        |----- module_3.py (Contains: print('module_3 at sub directory, is imported')) 
    |
    |------- subpackage_2/
    |           |
    |           |----- __init__.py (Empty file)
    |           |----- module_2.py (Contains: print('module_2 at same level directory, is imported'))

Ahora corre main_module.py

la salida es

>>>'importing other modules from module_1...'
   'module_0 at parent directory, is imported'
   'module_2 at same level directory, is imported'
   'module_3 at sub directory, is imported'

Abrir imágenes y archivos nota:

En una estructura de paquete, si desea acceder a una foto, use el directorio absoluto del directorio de nivel más alto.

Supongamos que estás corriendo main_module.pyy quieres abrir el photo.pnginterior module_1.py.

lo que module_1.pydebe contener es:

Correcto:

image_path = 'subpackage_1/photo.png'
cv2.imread(image_path)

Incorrecto:

image_path = 'photo.png'
cv2.imread(image_path)

aunque module_1.pyy photo.pngestán en el mismo directorio.

11

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 en sys.path(necesito rutas relativas porque uso mis scripts en varias PC y en diferentes directorios principales).

Y al usar Windows, ambos \y /se pueden usar como separadores para nombres de archivos y, por supuesto, debe duplicar \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 el \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)

1
  • 4
    os.path.join ('herramientas', 'mydir') 8 feb 2019 a las 2:00
11

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 la versión 3.X de Python porque Python 2 no es compatible desde el 1 de enero de 2020 .

Estructura del proyecto

En python 3, no es necesario __init__.pyen el subdirectorio de su proyecto debido a los paquetes de espacio de nombres implícitos . Consulte ¿ 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 Aen file_a.pyla carpeta a.

Soluciones

# 1 Una forma rápida pero sucia

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

Usando el try catchpara verificar 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)

Simplemente puedes

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!

3
  • para obtener más información sobre las importaciones absolutas
    WY Hsu
    5 de ene. De 2020 a las 9:36
  • 1
    su primera solución propuesta funcionó para mí usando sys.path.insert (1, '../a/') que creo que es mejor que escribir la ruta completa.
    Giacomo
    13 de marzo a las 18:49
  • En caso de que alguien tenga un paquete local que le gustaría importar en lugar del paquete del sistema (QUE TIENE EL MISMO NOMBRE), utilice sys.path.insert (1, 'folder-to-grab-package-from') en lugar de sys .append ('carpeta-para-agarrar-paquete-desde') 15 de marzo a las 11:02
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. Esta referencia de módulo tendrá prioridad sobre cualquier otro módulo instalado con el mismo nombre para cualquier script que se ejecute 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 sucursal de SVC local, lo que puede simplificar en gran medida el tiempo del ciclo de desarrollo y reducir los modos de falla de la administración de diferentes versiones del módulo.

5
├───root
│   ├───dir_a
│   │   ├───file_a.py
│   │   └───file_xx.py
│   ├───dir_b
│   │   ├───file_b.py
│   │   └───file_yy.py
│   ├───dir_c
│   └───dir_n

Puede agregar el directorio principal al PYTHONPATH, para lograrlo, puede usar la ruta según el sistema operativo en la "ruta de búsqueda del módulo" que se enumera en sys.path. Entonces puede agregar fácilmente el directorio principal como sigue:

# file_b.py

import sys
sys.path.insert(0, '..')

from dir_a.file_a import func_name
4

En lugar de simplemente hacer una import ..., haz esto:

from <MySubFolder> import <MyFile>

MyFile está dentro de MySubFolder.

3

Normalmente creo un enlace simbólico al módulo que quiero importar. El enlace simbólico se asegura de que el intérprete de Python pueda ubicar el módulo dentro del directorio actual (el script al que está importando el otro módulo); más adelante, cuando termine su trabajo, puede eliminar el enlace simbólico. Además, debe ignorar los enlaces simbólicos en .gitignore, para no enviar accidentalmente módulos con enlaces simbólicos a su repositorio. Este enfoque le permite incluso trabajar con éxito con módulos que están ubicados en paralelo al script que está ejecutando.

ln -s ~/path/to/original/module/my_module ~/symlink/inside/the/destination/directory/my_module
2

Estaba trabajando en un proyecto aque quería que los usuarios instalaran a través pip install ade 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

Luego, desde una ubicación totalmente diferente en mi sistema de archivos /moustache/armwrestle, pude ejecutar:

import a
dir(a)

Lo que confirmó que de a.catshecho era igual a 0 y de a.b.dogshecho era igual a 1, como se pretendía.

2

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

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 desde esta carpeta cambiando la scriptNamevariable. A continuación, puede utilizar 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

2

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

import sys
sys.insert(1, /path) 

hizo NO trabajar 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ón, supongo, sino:

Usé una 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')
2

Si tiene varias carpetas y subcarpetas, siempre puede importar cualquier clase o módulo desde el directorio principal .

Por ejemplo: estructura de árbol del proyecto

Project 
├── main.py
├── .gitignore
|
├── src
     ├────model
     |    └── user_model.py
     |────controller
          └── user_controller.py

Ahora, si desea importar la clase "UserModel" de user_model.py en el archivo main.py , puede hacerlo usando:

from src.model.user_model.py import UserModel

Además, puede importar la misma clase en el archivo user_controller.py usando la misma línea:

from src.model.user_model.py import UserModel

En general, puede dar una referencia del directorio principal del proyecto para importar clases y archivos en cualquier archivo de Python dentro del directorio del proyecto .

2
  • ¿Necesitamos __init__.pybajo src para que esto suceda? 17/12/20 a las 20:29
  • Esta no es una respuesta a la pregunta original que NO era sobre cómo importar desde main.py, sino más bien (siguiendo su ejemplo) desde user_model.py a user_controller.py.
    Giacomo
    13 de marzo a las 18:38
2

En caso de que alguien todavía esté buscando una solución. Esto funcionó para mí.

Python agrega la carpeta que contiene el script que inicia a PYTHONPATH, por lo que si ejecuta

python application/app2/some_folder/some_file.py

Solo se agrega la carpeta application / app2 / some_folder a la ruta (no el directorio base en el que está ejecutando el comando). En su lugar, ejecute su archivo como un módulo y agregue un __init__.py en su directorio some_folder.

python -m application.app2.some_folder.some_file

Esto agregará el directorio base a la ruta de Python, y luego se podrá acceder a las clases a través de una importación no relativa.

2

El siguiente código importa la secuencia de comandos de Python proporcionada por su ruta, sin importar dónde se encuentre, de una manera segura para la versión de Python:

def import_module_by_path(path):
    name = os.path.splitext(os.path.basename(path))[0]
    if sys.version_info[0] == 2:   
        # Python 2
        import imp
        return imp.load_source(name, path)
    elif sys.version_info[:2] <= (3, 4):  
        # Python 3, version <= 3.4
        from importlib.machinery import SourceFileLoader
        return SourceFileLoader(name, path).load_module()
    else:                            
        # Python 3, after 3.4
        import importlib.util
        spec = importlib.util.spec_from_file_location(name, path)
        mod = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(mod)
        return mod

Encontré esto en la base de código de psutils , en la línea 1042 en psutils.test.__init__.py( confirmación más reciente a partir del 09.10.2020 ).

Ejemplo de uso:

script = "/home/username/Documents/some_script.py"
some_module = import_module_by_path(script)
print(some_module.foo())

Advertencia importante: el módulo se tratará como de nivel superior; cualquier importación relativa de paquetes principales fallará.

3
  • ¿Alguna idea de por qué los dos métodos Python3 diferentes? Probé ambos en Python 3.6, y ambos funcionaron y arrojaron resultados idénticos
    Jon
    30 de mayo a las 22:50
  • También resultados idénticos en Python 3.8.9. A partir de 3.8.10 y posteriores, spec_from_file_locationcomienza a guardar la ruta raíz del archivo (si se proporciona una ruta relativa) en el objeto cargador , pero por lo demás, los datos devueltos son idénticos. También probado con Python 3.10 - exactamente el mismo comportamiento que 3.8.10. Ambos métodos funcionan bien.
    Jon
    30 de mayo a las 23:25
  • @Jon Desafortunadamente, no puedo comentar sobre esto, no estoy familiarizado con los matices de importlib. Esta es una propiedad encontrada, y no quería cambiar nada, pensé que tenían una razón para ello. Tal vez haya algún matiz que sea diferente, o que se rompa para versiones más antiguas o más nuevas. 31 de mayo a las 19:03 h.
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, entonces puede cambiar el directorio en sys, como dijo Cameron anteriormente.