Quiero heredar de una clase en un archivo que se encuentra en un directorio sobre el actual.
¿Es posible importar relativamente ese archivo?
Quiero heredar de una clase en un archivo que se encuentra en un directorio sobre el actual.
¿Es posible importar relativamente ese archivo?
Respuestas:
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
from
especificado, 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 ejecutafrom . import mod
desde un módulo en elpkg
paquete, terminará importandopkg.mod
. Si ejecutafrom ..subpkg2 import mod
desde dentropkg.subpkg1
, importarápkg.subpkg2.mod
. La especificación para las importaciones relativas está contenida en PEP 328 .
PEP 328 trata de importaciones absolutas / relativas.
import sys
sys.path.append("..") # Adds higher directory to python modules path.
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.dirname
llamadas 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
sys.path
que 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.
sys.path.remove(pathYouJustAdded)
después de la importación que necesitaba para no retener esta nueva ruta.
Importe el módulo desde un directorio que esté exactamente un nivel por encima del directorio actual:
from .. import module
from .. import module
recibí el error ValueError: Intenté la importación relativa en un paquete sin seguir la recomendación
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.
Es por eso que escribimos import foo
para 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
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 .foo
y..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
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 foo
funcione para otras computadoras (independientemente del intérprete, sistema operativo host, etc.)
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.py
como 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.py
bajo 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 foo
un "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 foo
se ejecutará foo/__main__.py
como __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:
foo
, aunque sustituir guiones de subrayado por guiones es popularpython ./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 install
la --target
opción, o virtualenv aka venv para clientes potenciales.
volviendo al ejemplo
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 baz
implementación relativa , podríamos usar una importación relativa como:
import ..baz
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.py
y foo/__init__.py
, mi breve ejemplo:
#!/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_project
en 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
#!/usr/bin/env python3
import bar
import foo
print(dir(bar))
print(dir(foo))
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.
editable
enlaces 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á!