¿Importar un archivo desde un subdirectorio?

456

Tengo un archivo llamado tester.py, ubicado en /project.

/projecttiene un subdirectorio llamado lib, con un archivo llamado BoxTime.py:

/project/tester.py
/project/lib/BoxTime.py

Quiero importar BoxTimedesde tester. He intentado esto:

import lib.BoxTime

Cual resulto:

Traceback (most recent call last):
  File "./tester.py", line 3, in <module>
    import lib.BoxTime
ImportError: No module named lib.BoxTime

¿Alguna idea de cómo importar BoxTimedesde el subdirectorio?

EDITAR

El __init__.pyera el problema, pero no se olvide de hacer referencia a BoxTimeque lib.BoxTime, o usar:

import lib.BoxTime as BT
...
BT.bt_function()
Adam Matan
fuente

Respuestas:

536

Eche un vistazo a la documentación de Paquetes (Sección 6.4) aquí: http://docs.python.org/tutorial/modules.html

En resumen, debe poner un archivo en blanco llamado

__init__.py

en el directorio "lib".

Greg
fuente
59
¿Por qué se siente hacky ? Es la forma en que Python marca los directorios de importación seguros / disponibles.
Resumen de
77
No solo marca los directorios de importación seguros / disponibles, sino que también proporciona una forma de ejecutar algún código de inicialización al importar un nombre de directorio.
Sadjad
32
Sí, esto es hacky e incluso sucio, y en mi opinión el lenguaje no debería imponer su forma de cargar archivos a través del sistema de archivos. En PHP resolvimos el problema dejando que el código de usuario registrara múltiples funciones de carga automática que se llaman cuando falta un espacio de nombres / clase. Luego, la comunidad ha producido el estándar PSR-4 y Composer lo implementa, y hoy en día nadie tiene que preocuparse por eso. Y no hay __init__archivos codificados estúpidos (pero si lo desea, ¡simplemente registre un enlace de carga automática! Esta es la diferencia entre hacky y hackable ).
Morgan Touverey Quilling
44
@ AurélienOomsimport sys, os; sys.path.insert(0, os.path.abspath('..')); from sibling_package.hacks import HackyHackHack
jbowman
44
python es desordenado :)
Jimmy Pettersson
174
  • Crea un subdirectorio llamado lib.
  • Crea un archivo vacío llamado lib\__init__.py.
  • En lib\BoxTime.py, escribe una función foo()como esta:

    def foo():
        print "foo!"
  • En su código de cliente en el directorio anterior lib, escriba:

    from lib import BoxTime
    BoxTime.foo()
  • Ejecute su código de cliente. Conseguirás:

    foo!

Mucho más tarde, en Linux, se vería así:

% cd ~/tmp
% mkdir lib
% touch lib/__init__.py
% cat > lib/BoxTime.py << EOF
heredoc> def foo():
heredoc>     print "foo!"
heredoc> EOF
% tree lib
lib
├── BoxTime.py
└── __init__.py

0 directories, 2 files
% python 
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from lib import BoxTime
>>> BoxTime.foo()
foo!
hughdbrown
fuente
2
¿Podría proporcionar un enlace a la documentación de Python donde se explica esto? ¡Gracias!
Zenon
55
Hagamos que se pueda
Gabriel Staples
Buen tutorial para implementar un paquetelib
MasterControlProgram
tenga en cuenta que las subdirecciones no deben contener guiones ni puntos, pero los guiones bajos son válidos. para mí eso parece ser las mismas restricciones que para otros nombres de símbolos, pero aún no lo he cavado hasta el nivel de documentación.
Alexander Stohr
guiones bajos => python3 (demasiado tarde para editar el comentario)
Alexander Stohr
68

Puedes intentar insertarlo en sys.path:

sys.path.insert(0, './lib')
import BoxTime
Kresimir
fuente
11
Esto es genial si por alguna razón no puedes o no quieres crear el archivo init .py.
jpihl
1
Funciona si ejecuta Python desde el directorio "proyecto". Los "." se interpreta en relación con su directorio de trabajo actual, no con respecto al directorio donde vive el archivo que está ejecutando. Puedes decir cd /data, python ../project/tester.py. Entonces no funcionará.
morningstar
2
Esto funcionó para mí. Prefiero esto sobre un archivo init .py, hace declaraciones de importación más limpias.
Taylor Evanson
55
Esto funciona MUCHO mejor y es la solución "correcta". init .py desordena paquetes como boto que tienen sus propias carpetas secundarias con módulos.
Dave Dopson el
1
@jpihl Debe crear (al menos) un archivo empy llamado __init__.py para permitir que Python importe módulos desde esa carpeta. He probado esa solución y funciona perfectamente (v2.7.6).
m3nda
31

Estoy escribiendo esto porque todos parecen sugerir que tienes que crear un libdirectorio.

No necesita nombrar su subdirectorio lib. Puedes nombrarlo anythingsiempre y cuando lo pongas __init__.py.

Puede hacerlo ingresando el siguiente comando en un shell de Linux:

$ touch anything/__init__.py 

Entonces ahora tienes esta estructura:

$ ls anything/
__init__.py
mylib.py

$ ls
main.py

A continuación, puede importar myliben main.pyeste aspecto:

from anything import mylib 

mylib.myfun()

También puede importar funciones y clases como esta:

from anything.mylib import MyClass
from anything.mylib import myfun

instance = MyClass()
result = myfun()

__init__.pyTambién se puede acceder a cualquier función o clase variable que coloque dentro :

import anything

print(anything.myvar)

O así:

from anything import myvar

print(myvar)
nurettin
fuente
Mi estructura de carpetas es utils\__init__.pyy utils\myfile.py. (Las utilidades contienen ambos archivos) Así es como estoy tratando de importar from utils.myfile import myMethod. Pero lo entiendo ModuleNotFoundError: No module named 'utils'. ¿Qué podría estar mal? PD: Estoy usando Djangoe intentando importar en el views.pyque está en el mismo nivel que la utilscarpeta
Jagruti
Es posible utilizar rutas absolutas al importar módulos y ejecutar su programa conPYTHONPATH=. python path/to/program.py
nurettin
21

¿Su directorio lib contiene un __init__.pyarchivo?

Python usa __init__.pypara determinar si un directorio es un módulo.

Vadear
fuente
16

Tratar import .lib.BoxTime. Para obtener más información, lea sobre la importación relativa en PEP 328 .

drrlvn
fuente
2
No creo haber visto esa sintaxis antes. ¿Hay alguna razón fuerte (no) para usar este método?
tgray
2
¿Por qué no era esta la respuesta? Claro, si quieres hacer todo el paquete, deberías hacerlo. Pero esa no era la pregunta original.
Travis Griggs
Esto me da: ValueError: intento de importación relativa en no paquete
Alex
55
Esto solo funciona si el archivo desde el que está importando forma parte de un paquete. De lo contrario, recibirá el error que @Alex señaló.
Jonathon Reinhart
8

Hago esto que básicamente cubre todos los casos (asegúrese de tener __init__.pyen la carpeta relativa / ruta / a / su / lib /):

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/your/lib/folder")
import someFileNameWhichIsInTheFolder
...
somefile.foo()


Ejemplo:
tiene en su carpeta de proyecto:

/root/myproject/app.py

Tienes en otra carpeta de proyecto:

/root/anotherproject/utils.py
/root/anotherproject/__init__.py

Desea usar /root/anotherproject/utils.pyy llamar a la función foo que está en ella.

Entonces escribes en app.py:

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../anotherproject")
import utils

utils.foo()
Mercurio
fuente
2
si está usando os.path, probablemente quiera usar en os.path.join((os.path.dirname(os.path.realpath(__file__)),'..','anotherproject')lugar de codificar '/' en la concatenación de ruta.
cowbert
¿Por qué no puedes "../anotherproject"prescindir del os.path.dirname()?
Moshe Rabaev
@MosheRabaev: es una buena práctica utilizar las funciones os.path. En caso de escribir "../anotherproject" y mover el código al sistema operativo Windows, ¡el código se romperá! os.path utils sabe cómo devolver la ruta correcta teniendo en cuenta el sistema operativo en el que se ejecuta el código. para más información docs.python.org/2/library/os.path.html
Mercury
@MosheRabaev y si usa ".." sin el dirname(realpath(__file__)), entonces calculará la ruta relativa a su directorio de trabajo actual cuando ejecute el script, no en relación con el lugar donde vive el script.
TJ Ellis
5

Cree un archivo vacío __init__.pyen el subdirectorio / lib. Y agregue al comienzo del código principal

from __future__ import absolute_import 

entonces

import lib.BoxTime as BT
...
BT.bt_function()

o mejor

from lib.BoxTime import bt_function
...
bt_function()
Mik
fuente
0

Solo una adición a estas respuestas.

Si desea importar todos los archivos de todos los subdirectorios , puede agregar esto a la raíz de su archivo.

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

Y luego simplemente puede importar archivos de los subdirectorios como si estos archivos estuvieran dentro del directorio actual.

Ejemplo de trabajo

Si tengo el siguiente directorio con subdirectorios en mi proyecto ...

.
├── a.py
├── b.py
├── c.py
├── subdirectory_a
   ├── d.py
   └── e.py
├── subdirectory_b
   └── f.py
├── subdirectory_c
   └── g.py
└── subdirectory_d
    └── h.py

Puedo poner el siguiente código dentro de mi a.pyarchivo

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

# And then you can import files just as if these files are inside the current directory

import b
import c
import d
import e
import f
import g
import h

En otras palabras, este código extraerá de qué directorio proviene el archivo.

Víctor
fuente
-1

/project/tester.py

/project/lib/BoxTime.py

cree un archivo en blanco __init__.pyen la línea hasta llegar al archivo

/project/lib/somefolder/BoxTime.py

#lib- necesidades tiene dos elementos uno __init__.pyy un directorio llamado somefolder #somefoldertiene dos elementos boxtime.pyy__init__.py

Chaitanya Gk
fuente
-3

prueba esto:

from lib import BoxTime

Orane
fuente
8
sin ninguna explicación esto no es muy útil.
Jean-François Fabre