Importar módulos desde la carpeta principal

627

Estoy ejecutando Python 2.5.

Este es mi árbol de carpetas:

ptdraft/
  nib.py
  simulations/
    life/
      life.py

(También tengo __init__.pyen cada carpeta, omitido aquí para facilitar la lectura)

¿Cómo importo el nibmódulo desde el interior del lifemódulo? Espero que sea posible hacerlo sin jugar con sys.path.

Nota: El módulo principal que se está ejecutando está en la ptdraftcarpeta.

Ram Rachum
fuente
1
¿Cuál es su configuración de PYTHONPATH?
S.Lott 03 de
2
Ross: miré allí. ¿Qué debo hacer al respecto? Ya tengo un __init__.py. S.Lott: No sé cómo verificar ...
Ram Rachum el
44
echo $ PYTHONPATH desde el shell; sistema de importación; imprima sys.path desde Python. docs.python.org/tutorial/…
S.Lott 03 de
@FlipMcF Google es un motor de búsqueda burbujeante, por lo que el hecho de que este resultado sea bastante alto para ti no importa. Mucho más importante es el hecho de que el motor de búsqueda sin burbujas, DuckDuckGo, también ocupa un lugar muy importante.
ArtOfWarfare
3
Recomiendo saltar todas sys.patho PYTHONPATHrespuestas y consultar la excelente respuesta de np8 . Sí, es una lectura larga. Sí, parece mucho trabajo. Pero es la única respuesta que realmente resuelve el problema de manera correcta y limpia.
Aran-Fey

Respuestas:

119

Parece que el problema no está relacionado con que el módulo esté en un directorio principal ni nada de eso.

Necesita agregar el directorio que contiene ptdrafta PYTHONPATH

Dijiste que import nibfuncionaba contigo, eso probablemente significa que te agregaste ptdraft(no a su padre) a PYTHONPATH.

Hasen
fuente
15
Tener que agregar todo a pythonpath no es una buena solución si está trabajando con muchos paquetes que nunca serán reutilizados.
Jason Cheng
@ JasonCheng: ¿Entonces por qué son paquetes ?
Davis Herring
77
Para mí esta respuesta ha funcionado. Sin embargo, para hacerlo más explícito, el procedimiento es import sysy luegosys.path.append("..\<parent_folder>")
BCJuan
604

Puede usar importaciones relativas (python> = 2.5):

from ... import nib

(Novedades de Python 2.5) PEP 328: importaciones absolutas y relativas

EDITAR : se agregó otro punto '.' subir dos paquetes

f3lix
fuente
178
ValueError: Attempted relative import in non-package
endolito
18
@endolith: debe estar en un paquete, es decir, debe tener un archivo init .py.
kirbyfan64sos
31
Intento de importación relativa más allá del paquete de nivel superior
Usuario
291
Para ser aún más preciso, necesita un __init__.pyarchivo.
kara deniz
17
Consulte también la siguiente respuesta, ya que agregar __init__.pyno es lo único que tiene que hacer: stackoverflow.com/questions/11536764/…
Ben Farmer,
286

Las importaciones relativas (como en from .. import mymodule) solo funcionan en un paquete. Para importar 'mymodule' que está en el directorio principal de su módulo actual:

import os,sys,inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0,parentdir) 

import mymodule

editar : el __file__atributo no siempre se da. En lugar de usar os.path.abspath(__file__), ahora sugerí usar el módulo de inspección para recuperar el nombre de archivo (y la ruta) del archivo actual

Remi
fuente
97
algo un poco más corto:sys.path.insert(1, os.path.join(sys.path[0], '..'))
JHolta
8
¿Alguna razón para evitar sys.path.append () en lugar de la inserción?
Tyler
2
@Tyler: puede importar si en otro lugar que no sea el parentdir, pero en una de las rutas ya especificadas sys.path, hay otro módulo con el nombre 'mymodule'. Insertar parentdircomo el primer elemento de la sys.pathlista asegura que el módulo parentdirse importará en su lugar.
Remi
55
@JHolta Si no está tratando con paquetes, el suyo es la mejor solución.
Jonathon Reinhart
55
@JHolta gran idea. sys.path.insert(1, os.path.realpath(os.path.pardir))también funciona
SpeedCoder5
179

Publiqué una respuesta similar también a la pregunta sobre las importaciones de paquetes hermanos. Puedes verlo aquí .

Solución sin sys.pathhacks

Resumen

  • Envuelva el código en una carpeta (por ejemplo packaged_stuff)
  • Use crear setup.pyscript donde use setuptools.setup () .
  • Pip instala el paquete en estado editable con pip install -e <myproject_folder>
  • Importar usando from packaged_stuff.modulename import function_name

Preparar

Asumo la misma estructura de carpetas que en la pregunta

.
└── ptdraft
    ├── __init__.py
    ├── nib.py
    └── simulations
        ├── __init__.py
        └── life
            ├── __init__.py
            └── life.py

Llamo a la .carpeta raíz, y en mi caso se encuentra en C:\tmp\test_imports.

Pasos

1) Agregue a setup.pya la carpeta raíz

El contenido del setup.pypuede ser simplemente

from setuptools import setup, find_packages

setup(name='myproject', version='1.0', packages=find_packages())

Básicamente "cualquiera" setup.pyfuncionaría. Este es solo un ejemplo de trabajo mínimo.

2) Use un entorno virtual

Si está familiarizado con los entornos virtuales, active uno y salte al siguiente paso. El uso de entornos virtuales no es absolutamente obligatorio, pero realmente lo ayudarán a largo plazo (cuando tenga más de 1 proyecto en curso ...). Los pasos más básicos son (ejecutar en la carpeta raíz)

  • Crea env virtual
    • python -m venv venv
  • Activar env virtual
    • . /venv/bin/activate(Linux) o ./venv/Scripts/activate(Win)

Para obtener más información sobre esto, solo busque en Google "python virtualenv tutorial" o similar. Probablemente nunca necesite más comandos que crear, activar y desactivar.

Una vez que haya creado y activado un entorno virtual, su consola debe indicar el nombre del entorno virtual entre paréntesis

PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>

3) pip instala tu proyecto en estado editable

Instale su paquete de nivel superior myprojectusando pip. El truco es usar la -ebandera al hacer la instalación. De esta forma, se instala en un estado editable, y todas las ediciones realizadas en los archivos .py se incluirán automáticamente en el paquete instalado.

En el directorio raíz, ejecute

pip install -e . (tenga en cuenta el punto, significa "directorio actual")

También puede ver que se instala utilizando pip freeze

(venv) PS C:\tmp\test_imports> pip install -e .
Obtaining file:///C:/tmp/test_imports
Installing collected packages: myproject
  Running setup.py develop for myproject
Successfully installed myproject
(venv) PS C:\tmp\test_imports> pip freeze
myproject==1.0

4) Importar anteponiendo mainfoldera cada importación

En este ejemplo, el mainfoldersería ptdraft. Esto tiene la ventaja de que no encontrará colisiones de nombres con otros nombres de módulos (desde la biblioteca estándar de Python o módulos de terceros).


Ejemplo de uso

nib.py

def function_from_nib():
    print('I am the return value from function_from_nib!')

life.py

from ptdraft.nib import function_from_nib

if __name__ == '__main__':
    function_from_nib()

Ejecutando life.py

(venv) PS C:\tmp\test_imports> python .\ptdraft\simulations\life\life.py
I am the return value from function_from_nib!
np8
fuente
1
¿Por qué funciona esto? ¿Está cambiando el PYTHONPATH detrás de escena de alguna manera?
Homero Esmeraldo
2
Además, ¿por qué es esto mejor o más elegante que usar el enfoque sys.path?
Homero Esmeraldo
55
@HomeroBarrocasSEsmeraldo Buenas preguntas. PYTHONPATH env var no ha sido tocado. Esto se instala en los paquetes del sitio , que ya están activados sys.pathy donde el código normalmente lo instalarían los usuarios finales. Eso es mejor que los sys.pathhacks (y puede incluir el uso de PYTHONPATH en esa categoría de hacks de ruta) porque significa que el código en desarrollo debería comportarse de la misma manera para usted que para otros usuarios. Por el contrario, cuando se utilizan modificaciones de ruta, las declaraciones de importación se resolverían de una manera diferente.
wim
55
Esta es, de lejos, la mejor solución que he leído. Esto crea un enlace a su directorio actual para que todas las actualizaciones del código se reflejen directamente en su directorio de trabajo. En caso de que necesite eliminar esta entrada, elimine manualmente el archivo egg y elimine la entrada de easy-install en dist-packages (paquetes de sitio si utilizó pip).
Rakshit Kothari
2
¿Por qué complicar aún más el dolor de cabeza de importación de Python? ¿Tenemos que escribir un archivo de configuración, un entorno virtual, una instalación de pip? ¡Dios mio!
Emerson Xu
68

Puede utilizar la ruta del sistema operativo en la "ruta de búsqueda del módulo" que se encuentra en sys.path . Por lo tanto, puede agregar fácilmente el directorio principal como el siguiente

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

Si desea agregar el directorio padre-padre,

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

Esto funciona tanto en python 2 como en 3.

Shinbero
fuente
66
Esto es relativo al directorio actual , ni siquiera necesariamente el que contiene el script principal.
Davis Herring
57

Si agregar la carpeta de su módulo a PYTHONPATH no funcionó, puede modificar la lista sys.path en su programa donde el intérprete de Python busca los módulos para importar, la documentación de python dice:

Cuando se importa un módulo llamado spam , el intérprete primero busca un módulo integrado con ese nombre. Si no se encuentra, busca un archivo llamado spam.py en una lista de directorios dada por la variable sys.path. 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 valor predeterminado dependiente de la instalación.

Después de la inicialización, los programas de Python pueden modificar sys.path . El directorio que contiene el script que se está ejecutando se coloca al comienzo de la ruta de búsqueda, por delante de la ruta de la biblioteca estándar. Esto significa que los scripts en ese directorio se cargarán en lugar de módulos del mismo nombre en el directorio de la biblioteca. Esto es un error a menos que se pretenda reemplazarlo.

Sabiendo esto, puede hacer lo siguiente en su programa:

import sys
# Add the ptdraft folder path to the sys.path list
sys.path.append('/path/to/ptdraft/')

# Now you can import your module
from ptdraft import nib
# Or just
import ptdraft
Ricardo Murillo
fuente
66
Su respuesta es buena, pero no siempre funciona y no es muy portátil. Si el programa se movió a una nueva ubicación, /path/to/ptdrafttendría que ser editado. Hay soluciones que también resuelven el directorio actual del archivo y lo importan desde la carpeta principal.
Edward
@ Edward ¿Cuáles son esas técnicas?
Shuklaswag
1
@Shuklaswag, por ejemplo, la respuesta stackoverflow.com/questions/714063/… .
Edward
50

No sé mucho sobre python 2.
En python 3, la carpeta principal se puede agregar de la siguiente manera:

import sys 
sys.path.append('..')

... y luego uno puede importar módulos desde él

Rob Ellenbroek
fuente
13
Esto solo funciona si su directorio de trabajo actual es tal que '..'conduce al directorio con el módulo en cuestión.
user5359531
31

Aquí hay una respuesta que es simple para que pueda ver cómo funciona, pequeña y multiplataforma.
Sólo se utiliza una función de módulos ( os, sysy inspect) así debería funcionar
en cualquier sistema operativo (OS), porque Python está diseñado para eso.

Código de respuesta más corto: menos líneas y variables

from inspect import getsourcefile
import os.path as path, sys
current_dir = path.dirname(path.abspath(getsourcefile(lambda:0)))
sys.path.insert(0, current_dir[:current_dir.rfind(path.sep)])
import my_module  # Replace "my_module" here with the module name.
sys.path.pop(0)

Para menos líneas que esta, reemplace la segunda línea con import os.path as path, sys, inspect,
agregue inspect.al comienzo de getsourcefile(línea 3) y elimine la primera línea.
- Sin embargo, esto importa todo el módulo, por lo que podría necesitar más tiempo, memoria y recursos.

El código para mi respuesta ( versión más larga )

from inspect import getsourcefile
import os.path
import sys

current_path = os.path.abspath(getsourcefile(lambda:0))
current_dir = os.path.dirname(current_path)
parent_dir = current_dir[:current_dir.rfind(os.path.sep)]

sys.path.insert(0, parent_dir)

import my_module  # Replace "my_module" here with the module name.

Utiliza un ejemplo de una respuesta de desbordamiento de pila ¿Cómo obtengo la ruta del
archivo ejecutado actual en Python?
para encontrar la fuente (nombre de archivo) del código en ejecución con una herramienta incorporada.

from inspect import getsourcefile  
from os.path import abspath  

A continuación, donde quiera encontrar el archivo fuente, simplemente use:

abspath(getsourcefile(lambda:0))

Mi código agrega una ruta de archivo a sys.pathla lista de rutas de Python
porque esto le permite a Python importar módulos desde esa carpeta.

Después de importar un módulo en el código, es una buena idea ejecutar sys.path.pop(0)en una nueva línea
cuando esa carpeta agregada tiene un módulo con el mismo nombre que otro módulo que se importa
más adelante en el programa. Debe eliminar el elemento de la lista agregado antes de la importación, no otras rutas.
Si su programa no importa otros módulos, es seguro no eliminar la ruta del archivo porque
después de que un programa finaliza (o reinicia el shell de Python), cualquier edición hecha para sys.pathdesaparecer.

Notas sobre una variable de nombre de archivo

Mi respuesta no usa la __file__variable para obtener la ruta del archivo / nombre de archivo del
código en ejecución porque los usuarios aquí a menudo lo han descrito como poco confiable . No debe usarlo
para importar módulos de la carpeta principal en programas utilizados por otras personas.

Algunos ejemplos en los que no funciona (cita de esta pregunta de desbordamiento de pila):

• no se puede encontrar en algunas plataformas • a veces no es la ruta completa del archivo

  • py2exeno tiene un __file__atributo, pero hay una solución alternativa
  • Cuando se ejecuta desde IDLE con execute()no hay __file__atributo
  • OS X 10.6 donde consigo NameError: global name '__file__' is not defined
Eduardo
fuente
1
Hice esto y eliminé la nueva entrada de ruta sys.path.pop(0)inmediatamente después de importar el módulo deseado. Sin embargo, las importaciones subsiguientes todavía fueron a esa ubicación. Por ejemplo, tengo app/config, app/toolsy app/submodule/config. Desde submodule, inserto app/para importar tools, luego elimino app/e intento importar config, pero obtengo en app/configlugar de app/submodule/config.
user5359531
Me di cuenta de que una de toolslas importaciones también estaba importando configdesde el directorio principal. Entonces, cuando más tarde traté de hacer sys.path.pop(0); import configadentro submodule, esperando llegar app/submodule/config, en realidad estaba entrando app/config. Evidentemente, Python devuelve una versión en caché de un módulo con el mismo nombre en lugar de buscar sys.pathun módulo que coincida con ese nombre. sys.pathen este caso se estaba modificando correctamente, Python simplemente no lo estaba verificando debido a que el módulo con el mismo nombre ya se había cargado.
user5359531
Creo que esta es la referencia exacta al problema que tuve: docs.python.org/3/reference/import.html#the-module-cache
user5359531
Alguna información útil de 5.3.1. El caché del módulo en la página de documentación : durante la importación, el nombre del módulo se busca en sys.modules ... sys.modules se puede escribir. Eliminar una clave invalidará la entrada de caché para el módulo con nombre, haciendo que Python busque nuevamente en su próxima importación. Sin embargo, tenga cuidado, ya que si mantiene una referencia al objeto del módulo, invalida su caché y luego vuelve a importar, los dos objetos serán diferentes. Por el contrario, importlib.reload()reutilizará y reiniciará el contenido del módulo ...
Edward
Aún más corto: os.path.realpath(getsourcefile(lambda:0) + "/../.."). Obtendrá el archivo fuente actual junto con dos símbolos del directorio principal. No utilicé os.path.sepcomo getsourcefileestaba devuelve una cadena /incluso en Windows. realpathse encargará de extraer dos entradas de directorio de la ruta completa (en este caso, el nombre de archivo y luego el directorio actual) que proporciona el directorio principal.
Coburn
28

Aquí hay una solución más genérica que incluye el directorio padre en sys.path (funciona para mí):

import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
Miguel
fuente
import os, sys\n sys.path.insert(0,os.path.pardir) lo mismo, pero más allá :) (no hay avances de línea en los comentarios)
Anti Veeranna
@antiveeranna, si usas os.path.pardirno obtendrás el camino sys.path.insert()
real
1
Esta respuesta utiliza la __file__variable que puede ser poco confiable (no siempre es la ruta completa del archivo, no funciona en todos los sistemas operativos, etc.) como los usuarios de StackOverflow han mencionado a menudo. Cambiar la respuesta para que no se incluya causará menos problemas y será más compatible. Para obtener más información, consulte stackoverflow.com/a/33532002/3787376 .
Edward
23

Encontré que la siguiente forma funciona para importar un paquete desde el directorio principal del script. En el ejemplo, me gustaría importar funciones env.pydesde el app.dbpaquete.

.
└── my_application
    └── alembic
        └── env.py
    └── app
        ├── __init__.py
        └── db
import os
import sys
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)
Yu N.
fuente
Por lo tanto, una buena de una sola línea:sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
juzzlin
La modificación sys.pathen general suele ser una mala idea. Necesita alguna forma ( p . Ej . PYTHONPATH) Para que su biblioteca sea accesible a otro código (o de lo contrario no es realmente una biblioteca); solo usa eso todo el tiempo!
Davis Herring
14

Las soluciones mencionadas anteriormente también están bien. Otra solución a este problema es

Si desea importar algo desde el directorio de nivel superior. Entonces,

from ...module_name import *

Además, si desea importar algún módulo desde el directorio principal. Entonces,

from ..module_name import *

Además, si desea importar algún módulo desde el directorio principal. Entonces,

from ...module_name.another_module import *

De esta manera, puede importar cualquier método en particular si lo desea.

Ravin Gupta
fuente
66
Esto parece ser como debería ser, pero por alguna razón esto no funciona de la misma manera que los sys.path.appendhacks anteriores y no puedo importar mi lib de esta manera. Esto es lo que intenté hacer antes de comenzar a google :) Ok, fue estoValueError: attempted relative import beyond top-level package
juzzlin
10

Para mí, el más corto y mi línea favorita para acceder al directorio principal es:

sys.path.append(os.path.dirname(os.getcwd()))

o:

sys.path.insert(1, os.path.dirname(os.getcwd()))

os.getcwd () devuelve el nombre del directorio de trabajo actual, os.path.dirname (nombre_directorio) devuelve el nombre del directorio para el pasado.

En realidad, en mi opinión, la arquitectura del proyecto Python debe hacerse de la manera en que ningún módulo del directorio secundario utilizará ningún módulo del directorio principal. Si sucede algo como esto, vale la pena repensar sobre el árbol del proyecto.

Otra forma es agregar el directorio padre a la variable de entorno del sistema PYTHONPATH.

zviad
fuente
2
+1 por mencionar la posibilidad de tener que reestructurar el proyecto (en lugar de lanzar un truco al problema)
semore_1267
esto no consigue al padre, recibe el actual
Ricky Levi
9

En un cuaderno de Jupyter

Mientras trabaje en un Jupyter Notebook, esta breve solución puede ser útil:

%cd ..
import nib

Funciona incluso sin un __init__.pyarchivo.

Lo probé con Anaconda3 en Linux y Windows 7.

Thomas R
fuente
2
¿No tendría sentido agregar %cd -después de la importación, por lo que el directorio actual de la notebook no se modifica?
Dror
8

import sys sys.path.append('../')

Andong Zhan
fuente
8
esto no funcionará cuando se ejecute desde un directorio diferente.
Shatlyk Ashyralyyev
5

Cuando no se encuentra en un entorno de paquete con __init__.pyarchivos, la biblioteca pathlib (incluida con> = Python 3.4) hace que sea muy conciso e intuitivo agregar la ruta del directorio principal a PYTHONPATH:

import sys
from pathlib import Path
sys.path.append(str(Path('.').absolute().parent))
importa
fuente
¿Es posible usar init .py para evitar este problema?
Inteligencia artificial
4

mismo tipo de estilo que la respuesta anterior, pero en menos líneas: P

import os,sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0,parentdir)

el archivo devuelve la ubicación en la que está trabajando

YFP
fuente
Para mí, el archivo es el nombre de archivo sin la ruta incluida. Lo ejecuto usando "ipy filename.py".
Curtis Yallop
Hola @CurtisYallop en el ejemplo anterior, estamos agregando el directorio que contiene el archivo [en el que estamos actualmente] en el que se encuentra el archivo python. La llamada os.path.dirname con el nombre del archivo debería devolver la ruta del archivo y nosotros están agregando ESO a la ruta y no al archivo explícitamente - HTH :-)
YFP
Asume que __file__ siempre contiene la ruta más el archivo. A veces contiene solo el nombre del archivo sin la ruta.
Curtis Yallop
@CurtisYallop: no, señor, estoy asumiendo que el archivo es el nombre del archivo y estoy usando os.path.dirname () para obtener la ruta del archivo. ¿Tienes un ejemplo de esto que no funciona? Me encantaría los pasos para reproducirme
YFP
1
@CurtisYallop - ¡No, no lo soy! Estoy diciendo que: print / _ / file / _ / le dará el nombre de archivo en su ejemplo arriba de "file.py" - Luego estoy diciendo que puede usar os.path.dirname () para extraer la ruta completa de ese . Si está llamando desde una ubicación extraña y le gustaría tener una relación relacional, puede encontrar fácilmente su directorio de trabajo a través del módulo os: ¡Dios!
YFP
1

Trabajar con bibliotecas. Haga una biblioteca llamada nib, instálela usando setup.py, deje que resida en paquetes de sitio y sus problemas se resolverán. No tienes que rellenar todo lo que haces en un solo paquete. Rómpelo en pedazos.

usuario3181121
fuente
1
Su idea es buena, pero algunas personas pueden querer agrupar su módulo con su código, tal vez para que sea portátil y no requiera colocar sus módulos site-packagescada vez que lo ejecuten en una computadora diferente.
Edward
0

En un sistema Linux, puede crear un enlace suave desde la carpeta "vida" al archivo nib.py. Luego, simplemente puede importarlo como:

import nib
Erel Segal-Halevi
fuente