¿Cómo importar una clase de Python que está en un directorio de arriba?

213

Quiero heredar de una clase en un archivo que se encuentra en un directorio sobre el actual.

¿Es posible importar relativamente ese archivo?

usuario129975
fuente

Respuestas:

177

from ..subpkg2 import mod

Según los documentos de Python: cuando esté dentro de una jerarquía de paquetes, use dos puntos, como dice el documento de la declaración de importación :

Al especificar qué módulo importar no tiene que especificar el nombre absoluto del módulo. Cuando un módulo o paquete está contenido dentro de otro paquete, es posible realizar una importación relativa dentro del mismo paquete superior sin tener que mencionar el nombre del paquete. Al utilizar puntos iniciales en el módulo o paquete fromespecificado, puede especificar qué tan alto recorrer la jerarquía de paquetes actual sin especificar nombres exactos. Un punto inicial significa el paquete actual donde existe el módulo que realiza la importación. Dos puntos significan un nivel de paquete . Tres puntos son dos niveles, etc. Entonces, si ejecuta from . import moddesde un módulo en el pkgpaquete, terminará importando pkg.mod. Si ejecuta from ..subpkg2 import moddesde dentro pkg.subpkg1, importarápkg.subpkg2.mod. La especificación para las importaciones relativas está contenida en PEP 328 .

PEP 328 trata de importaciones absolutas / relativas.

gimel
fuente
44
up1 = os.path.abspath ('..') sys.path.insert (0, up1)
rp.
Pep 328 muestra solo la versión Python: 2.4, 2,5, 2.6. La versión 3 se deja a las almas más informadas.
gimel
22
Esto desencadena el error ValueError: intento de importación relativa más allá del paquete de nivel superior
Carlo
44
Resultados en ImportError: intento de importación relativa sin paquete padre conocido
Rotkiv
116
import sys
sys.path.append("..") # Adds higher directory to python modules path.
Sepero
fuente
1
esto funciona para mi Después de agregar esto, puedo importar directamente los módulos principales, sin necesidad de usar "..".
Evan Hu
8
eso solo funciona, en términos generales, si el valor PWD de la aplicación, su directorio actual, es hijo del padre. Entonces, incluso si funciona, muchos tipos de cambios sistémicos pueden desalojarlo.
Phlip
Esto funcionó para mí también para importar módulos de nivel superior. Lo uso con os.chdir ("..") para cargar otros archivos de nivel superior.
Surendra Shrestha
Esto parece desencadenar el mismo error que la respuesta anterior de gimel.
Carlo
81

La respuesta de @ gimel es correcta si puede garantizar la jerarquía de paquetes que menciona. Si no puede, si su necesidad real es como la expresó, vinculada exclusivamente a los directorios y sin ninguna relación necesaria con el empaquetado, entonces necesita trabajar __file__para encontrar el directorio principal (un par de os.path.dirnamellamadas funcionarán; -), entonces (si ese directorio no está ya en sys.path) prepend temporalmente dicho inserto dir en el comienzo mismo de sys.path, __import__, retire dijo dir de nuevo - el trabajo sucio de hecho, pero, "cuando es necesario, debe" (y se esfuerza a Phyton nunca detenga al programador de hacer lo que debe hacerse, ¡como dice el estándar ISO C en la sección "Espíritu de C" en su prefacio! -).

Aquí hay un ejemplo que puede funcionar para usted:

import sys
import os.path
sys.path.append(
    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))

import module_in_parent_dir
Alex Martelli
fuente
2
Esto podría agregar un directorio que está dentro de un paquete de Python para sys.pathque el mismo módulo esté disponible con diferentes nombres y todos los errores correspondientes. autopath.py en pypy o _preamble.py en retorcido lo resuelven usando un criterio de búsqueda que identifica el paquete de nivel superior mientras recorre los directorios hacia arriba.
jfs
44
Es posible que desee hacer algo como sys.path.remove(pathYouJustAdded)después de la importación que necesitaba para no retener esta nueva ruta.
Matthias
35

Importe el módulo desde un directorio que esté exactamente un nivel por encima del directorio actual:

from .. import module
Sepero
fuente
38
Obtuve: intento de importación relativa más allá del paquete de nivel superior :(
RicardoE
25
usando from .. import module recibí el error ValueError: Intenté la importación relativa en un paquete sin seguir la recomendación
user3428154
4

Cómo cargar un módulo que es un directorio arriba

Prefacio: Realicé una reescritura sustancial de una respuesta anterior con la esperanza de ayudar a las personas a entrar en el ecosistema de Python y, con suerte, darles a todos el mejor cambio de éxito con el sistema de importación de Python.

Esto cubrirá las importaciones relativas dentro de un paquete , que creo que es el caso más probable de la pregunta de OP.

Python es un sistema modular

Es por eso que escribimos import foopara cargar un módulo "foo" desde el espacio de nombres raíz, en lugar de escribir:

foo = dict();  # please avoid doing this
with open(os.path.join(os.path.dirname(__file__), '../foo.py') as foo_fh:  # please avoid doing this
    exec(compile(foo_fh.read(), 'foo.py', 'exec'), foo)  # please avoid doing this

Python no está acoplado a un sistema de archivos

Es por eso que podemos incrustar python en un entorno donde no hay un sistema de archivos de facto sin proporcionar uno virtual, como Jython.

Estar desacoplado de un sistema de archivos permite que las importaciones sean flexibles, este diseño permite cosas como importaciones de archivos comprimidos / zip, importación de singletons, almacenamiento en caché de byte, extensiones cffi, incluso la carga remota de definiciones de código.

Entonces, si las importaciones no están acopladas a un sistema de archivos, ¿qué significa "un directorio arriba"? Tenemos que elegir algunas heurísticas, pero podemos hacerlo, por ejemplo, cuando trabajamos dentro de un paquete , ya se han definido algunas heurísticas que hacen que a las importaciones relativas les guste .fooy..foo funcionen dentro del mismo paquete. ¡Frio!

Si sinceramente desea acoplar sus patrones de carga de código fuente a un sistema de archivos, puede hacerlo. Tendrá que elegir sus propias heurísticas y utilizar algún tipo de maquinaria de importación. Recomiendo importlib

El ejemplo importlib de Python se ve así:

import importlib.util
import sys

# For illustrative purposes.
file_path = os.path.join(os.path.dirname(__file__), '../foo.py')
module_name = 'foo'

foo_spec = importlib.util.spec_from_file_location(module_name, file_path)
# foo_spec is a ModuleSpec specifying a SourceFileLoader
foo_module = importlib.util.module_from_spec(foo_spec)
sys.modules[module_name] = foo_module
foo_spec.loader.exec_module(foo_module)

foo = sys.modules[module_name]
# foo is the sys.modules['foo'] singleton

embalaje

Hay un gran proyecto de ejemplo disponible oficialmente aquí: https://github.com/pypa/sampleproject

Un paquete de Python es una recopilación de información sobre su código fuente, que puede informar a otras herramientas cómo copiar su código fuente en otras computadoras y cómo integrar su código fuente en la ruta de ese sistema para que import foofuncione para otras computadoras (independientemente del intérprete, sistema operativo host, etc.)

Estructura de directorios

Tengamos un nombre de paquete foo, en algún directorio (preferiblemente un directorio vacío).

some_directory/
    foo.py  # `if __name__ == "__main__":`  lives here

Mi preferencia es crear setup.pycomo hermano foo.py, porque hace que escribir el archivo setup.py sea más simple, sin embargo, puede escribir la configuración para cambiar / redirigir todo lo que setuptools hace de manera predeterminada si lo desea; por ejemplo, poner foo.pybajo un directorio "src /" es algo popular, no cubierto aquí.

some_directory/
    foo.py
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    py_modules=['foo'],
)

.

python3 -m pip install --editable ./  # or path/to/some_directory/

"editable", también conocido -e, redirigirá una vez más la maquinaria de importación para cargar los archivos de origen en este directorio, en lugar de copiar los archivos exactos actuales en la biblioteca del entorno de instalación. Esto también puede causar diferencias de comportamiento en la máquina de un desarrollador, ¡asegúrese de probar su código! Hay otras herramientas además de pip, sin embargo, recomendaría que pip sea el introductorio :)

También me gusta hacer fooun "paquete" (un directorio que contenga __init__.py) en lugar de un módulo (un solo archivo ".py"), tanto "paquetes" como "módulos" se pueden cargar en el espacio de nombres raíz, los módulos permiten espacios de nombres anidados, lo cual es útil si queremos tener una importación "relativa a un directorio arriba".

some_directory/
    foo/
        __init__.py
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
)

También me gusta hacer un foo/__main__.py, esto permite que Python ejecute el paquete como un módulo, por ejemplo, python3 -m foose ejecutará foo/__main__.pycomo __main__.

some_directory/
    foo/
        __init__.py
        __main__.py  # `if __name__ == "__main__":`  lives here, `def main():` too!
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
    ...
    entry_points={
        'console_scripts': [
            # "foo" will be added to the installing-environment's text mode shell, eg `bash -c foo`
            'foo=foo.__main__:main',
        ]
    },
)

Vamos a desarrollar esto con algunos módulos más: Básicamente, puede tener una estructura de directorios como esta:

some_directory/
    bar.py           # `import bar`
    foo/
        __init__.py  # `import foo`
        __main__.py
        baz.py       # `import foo.baz
        spam/           
            __init__.py  # `import foo.spam`
            eggs.py      # `import foo.spam.eggs`
    setup.py

setup.py convencionalmente contiene información de metadatos sobre el código fuente, como:

  • qué dependencias se necesitan para instalar llamadas "install_requires"
  • qué nombre se debe usar para la administración de paquetes ("nombre" de instalación / desinstalación), sugiero que coincida con el nombre de su paquete de Python principal en nuestro caso foo , aunque sustituir guiones de subrayado por guiones es popular
  • información de licencia
  • etiquetas de madurez (alfa / beta / etc),
  • etiquetas de audiencia (para desarrolladores, para aprendizaje automático, etc.),
  • contenido de documentación de una sola página (como un archivo README),
  • nombres de shell (nombres que escribe en el shell de usuario como bash, o nombres que encuentra en un shell de usuario gráfico como un menú de inicio),
  • Una lista de módulos de Python que este paquete instalará (y desinstalará)
  • un punto de entrada de facto "ejecutar pruebas" python ./setup.py test

Es muy expansivo, incluso puede compilar extensiones C sobre la marcha si se instala un módulo fuente en una máquina de desarrollo. Para un ejemplo de todos los días, recomiendo setup.py del repositorio de muestras PYPA.

Si está lanzando un artefacto de compilación, por ejemplo, una copia del código destinado a ejecutar computadoras casi idénticas, un archivo require.txt es una forma popular de capturar información de dependencia exacta, donde "install_requires" es una buena manera de capturar el mínimo y Máximas versiones compatibles. Sin embargo, dado que las máquinas de destino son casi idénticas de todos modos, recomiendo crear un tarball de un prefijo completo de python. Esto puede ser complicado, demasiado detallado para entrar aquí. Echa un vistazo a pip installla --targetopción, o virtualenv aka venv para clientes potenciales.

volviendo al ejemplo

cómo importar un archivo un directorio arriba:

Desde foo / spam / eggs.py, si quisiéramos un código de foo / baz, podríamos solicitarlo por su espacio de nombres absoluto:

import foo.baz

Si quisiéramos reservar la capacidad de mover eggs.py a otro directorio en el futuro con alguna otra bazimplementación relativa , podríamos usar una importación relativa como:

import ..baz
ThorSummoner
fuente
-5

Python es un sistema modular

Python no se basa en un sistema de archivos

Para cargar el código de Python de manera confiable, tenga ese código en un módulo y el módulo instalado en la biblioteca de Python.

Los módulos instalados siempre se pueden cargar desde el espacio de nombres de nivel superior con import <name>


Hay un gran proyecto de muestra disponible oficialmente aquí: https://github.com/pypa/sampleproject

Básicamente, puede tener una estructura de directorios como esta:

the_foo_project/
    setup.py  

    bar.py           # `import bar`
    foo/
      __init__.py    # `import foo`

      baz.py         # `import foo.baz`

      faz/           # `import foo.faz`
        __init__.py
        daz.py       # `import foo.faz.daz` ... etc.

.

Asegúrese de declarar su setuptools.setup()en setup.py,

ejemplo oficial: https://github.com/pypa/sampleproject/blob/master/setup.py

En nuestro caso, probablemente queremos exportar bar.pyy foo/__init__.py, mi breve ejemplo:

setup.py

#!/usr/bin/env python3

import setuptools

setuptools.setup(
    ...
    py_modules=['bar'],
    packages=['foo'],
    ...
    entry_points={}, 
        # Note, any changes to your setup.py, like adding to `packages`, or
        # changing `entry_points` will require the module to be reinstalled;
        # `python3 -m pip install --upgrade --editable ./the_foo_project
)

.

Ahora podemos instalar nuestro módulo en la biblioteca de Python; con pip, puede instalarlo the_foo_projecten su biblioteca de python en modo de edición, para que podamos trabajar en él en tiempo real

python3 -m pip install --editable=./the_foo_project

# if you get a permission error, you can always use 
# `pip ... --user` to install in your user python library

.

Ahora desde cualquier contexto de Python, podemos cargar nuestros módulos y paquetes py_ compartidos

foo_script.py

#!/usr/bin/env python3

import bar
import foo

print(dir(bar))
print(dir(foo))
ThorSummoner
fuente
2
Debería mencionar que siempre instalo mi módulo mientras trabajo con él pip install --edit foo, casi siempre dentro de un virtualenv. Casi nunca escribo un módulo que no está destinado a ser instalado. Si no entiendo algo que me gustaría saber.
ThorSummoner
También debo mencionar que usar un conjunto de pruebas de creación automática de venv, como tox, es muy útil ya que los editableenlaces de huevo y los módulos de python instalados no son exactamente iguales en todos los sentidos; por ejemplo, agregar un nuevo espacio de nombres a un módulo editable se encontrará en su búsqueda de ruta, pero si eso no se exporta en su archivo setup.py, ¡no se empaquetará / instalará!
Pon a