python - ¿Qué hace realmente__future__ import absolute_import?
python-2.7 python-import (2)
answered
una pregunta sobre las importaciones absolutas en Python, que pensé que entendía en base a leer
el registro de cambios de Python 2.5
y el
PEP
acompañaba.
Sin embargo, al instalar Python 2.5 e intentar crear un ejemplo de uso adecuado
from __future__ import absolute_import
, me doy cuenta de que las cosas no están tan claras.
Directamente desde el registro de cambios vinculado anteriormente, esta declaración resumió con precisión mi comprensión del cambio absoluto de importación:
Digamos que tiene un directorio de paquetes como este:
pkg/ pkg/__init__.py pkg/main.py pkg/string.py
Esto define un paquete llamado
pkg
contiene los submódulospkg.main
ypkg.string
.Considere el código en el módulo main.py. ¿Qué sucede si ejecuta la
import string
instrucciones? En Python 2.4 y versiones anteriores, primero buscará en el directorio del paquete para realizar una importación relativa, encuentra pkg / string.py, importa el contenido de ese archivo como el módulopkg.string
y ese módulo está vinculado al nombre"string"
en el espacio de nombres del módulopkg.main
.
Entonces creé esta estructura de directorio exacta:
$ ls -R
.:
pkg/
./pkg:
__init__.py main.py string.py
__init__.py
y
string.py
están vacías.
main.py
contiene el siguiente código:
import string
print string.ascii_uppercase
Como se esperaba, ejecutar esto con Python 2.5 falla con un
AttributeError
:
$ python2.5 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print string.ascii_uppercase
AttributeError: ''module'' object has no attribute ''ascii_uppercase''
Sin embargo, más adelante en el registro de cambios 2.5, encontramos esto (énfasis agregado):
En Python 2.5, puede cambiar el comportamiento de la importación a importaciones absolutas usando una
from __future__ import absolute_import
. Este comportamiento de importación absoluta se convertirá en el predeterminado en una versión futura (probablemente Python 2.7). Una vez que las importaciones absolutas son las predeterminadas, laimport string
siempre encontrará la versión de la biblioteca estándar.
Así creé
pkg/main2.py
, idéntico a
main.py
pero con la directiva de importación futura adicional.
Ahora se ve así:
from __future__ import absolute_import
import string
print string.ascii_uppercase
Ejecutar esto con Python 2.5, sin embargo ... falla con un
AttributeError
:
$ python2.5 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print string.ascii_uppercase
AttributeError: ''module'' object has no attribute ''ascii_uppercase''
Esto contradice la afirmación de que
import string
siempre
encontrará la versión std-lib con las importaciones absolutas habilitadas.
Además, a pesar de la advertencia de que las importaciones absolutas están programadas para convertirse en el "nuevo comportamiento predeterminado", me encontré con este mismo problema usando Python 2.7, con o sin la directiva
__future__
:
$ python2.7 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print string.ascii_uppercase
AttributeError: ''module'' object has no attribute ''ascii_uppercase''
$ python2.7 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print string.ascii_uppercase
AttributeError: ''module'' object has no attribute ''ascii_uppercase''
así como Python 3.5, con o sin (suponiendo que la declaración de
print
se cambie en ambos archivos):
$ python3.5 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print(string.ascii_uppercase)
AttributeError: module ''string'' has no attribute ''ascii_uppercase''
$ python3.5 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print(string.ascii_uppercase)
AttributeError: module ''string'' has no attribute ''ascii_uppercase''
He probado otras variaciones de esto.
En lugar de
string.py
, he creado un módulo vacío, un directorio llamado
string
contiene solo un
__init__.py
vacío, y en lugar de emitir importaciones desde
main.py
, tengo
cd
''d a
pkg
y ejecuto las importaciones directamente desde el REPL.
Ninguna de estas variaciones (ni una combinación de ellas) cambió los resultados anteriores.
No puedo conciliar esto con lo que he leído sobre la directiva
__future__
y las importaciones absolutas.
Me parece que esto es fácilmente explicable por lo siguiente (esto es de los documentos de Python 2, pero esta declaración permanece sin cambios en los mismos documentos para Python 3):
sys.path
(...)
Como se inicializó al iniciar el programa, el primer elemento de esta lista,
path[0]
, es el directorio que contiene el script que se utilizó para invocar al intérprete de Python. Si el directorio del script no está disponible (por ejemplo, si el intérprete se invoca de forma interactiva o si el script se lee desde la entrada estándar), lapath[0]
es la cadena vacía, que dirige a Python a buscar módulos en el directorio actual primero.
Entonces, ¿qué me estoy perdiendo?
¿Por qué la declaración
__future__
aparentemente no hace lo que dice, y cuál es la resolución de esta contradicción entre estas dos secciones de documentación, así como entre el comportamiento descrito y el real?
El registro de cambios está redactado descuidadamente.
from __future__ import absolute_import
no le importa si algo es parte de la biblioteca estándar, y
import string
no siempre le dará el módulo de biblioteca estándar con importaciones absolutas.
from __future__ import absolute_import
significa que si
import string
, Python siempre buscará un módulo de
string
nivel superior, en lugar de
current_package.string
.
Sin embargo, no afecta la lógica que Python usa para decidir qué archivo es el módulo de
string
.
Cuando tu lo hagas
python pkg/script.py
pkg/script.py
no parece parte de un paquete para Python.
Siguiendo los procedimientos normales, el directorio
pkg
se agrega a la ruta y todos los archivos
.py
en el directorio
pkg
parecen módulos de nivel superior.
import string
encuentra
pkg/string.py
no porque esté haciendo una importación relativa, sino porque
pkg/string.py
parece ser la
string
módulo de nivel superior.
El hecho de que este no es el módulo de
string
biblioteca estándar no surge.
Para ejecutar el archivo como parte del paquete
pkg
, puede hacer
python -m pkg.script
En este caso, el directorio
pkg
no se agregará a la ruta.
Sin embargo, el directorio actual se agregará a la ruta.
También puede agregar algo repetitivo a
pkg/script.py
para que Python lo trate como parte del paquete
pkg
incluso cuando se ejecuta como un archivo:
if __name__ == ''__main__'' and __package__ is None:
__package__ = ''pkg''
Sin embargo, esto no afectará a
sys.path
.
Necesitará un manejo adicional para eliminar el directorio
pkg
de la ruta, y si el directorio padre de
pkg
no está en la ruta, también deberá pegarlo en la ruta.
La diferencia entre las importaciones absolutas y relativas entra en juego solo cuando importa un módulo de un paquete y ese módulo importa otro submódulo de ese paquete. Ver la diferencia:
$ mkdir pkg
$ touch pkg/__init__.py
$ touch pkg/string.py
$ echo ''import string;print(string.ascii_uppercase)'' > pkg/main1.py
$ python2
Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pkg.main1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pkg/main1.py", line 1, in <module>
import string;print(string.ascii_uppercase)
AttributeError: ''module'' object has no attribute ''ascii_uppercase''
>>>
$ echo ''from __future__ import absolute_import;import string;print(string.ascii_uppercase)'' > pkg/main2.py
$ python2
Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pkg.main2
ABCDEFGHIJKLMNOPQRSTUVWXYZ
>>>
En particular:
$ python2 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 1, in <module>
from __future__ import absolute_import;import string;print(string.ascii_uppercase)
AttributeError: ''module'' object has no attribute ''ascii_uppercase''
$ python2
Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pkg.main2
ABCDEFGHIJKLMNOPQRSTUVWXYZ
>>>
$ python2 -m pkg.main2
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Tenga en cuenta que
python2 pkg/main2.py
tiene un comportamiento diferente al iniciar
python2
y luego importar
pkg.main2
(que es equivalente a usar el
pkg.main2
-m
).
Si alguna vez desea ejecutar un submódulo de un paquete, use siempre el
sys.path
-m
que evita que el intérprete cambie la lista
sys.path
y maneje correctamente la semántica del submódulo.
Además, prefiero usar importaciones relativas explícitas para submódulos de paquetes, ya que proporcionan más semántica y mejores mensajes de error en caso de falla.