He respondido a una pregunta con respecto a las importaciones absolutas en Python, que pensé que comprendía basado en la lectura de la lista de cambios Python 2.5 y acompañando PEP . 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 que
pkg
contiene los submódulospkg.main
ypkg.string
.Considere el código en el módulo main.py. ¿Qué sucede si ejecuta la declaración
import string
? 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 comopkg.string
módulo y ese módulo está vinculado al nombre"string"
en elpkg.main
espacio de nombres del módulo.
Entonces creé esta estructura de directorio exacta:
$ ls -R
.:
pkg/
./pkg:
__init__.py main.py string.py
__init__.py
y string.py
estan vacios 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
import
el comportamiento de las importaciones absolutas utilizando unafrom __future__ import absolute_import
directiva. 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,import string
siempre encontrará la versión de la biblioteca estándar.
Así creé pkg/main2.py
, idéntico main.py
pero con la futura directiva de importación 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 bastante rotundamente que import string
será siempre encontrar la versión STD-lib con las importaciones absolutos habilitadas. Además, a pesar de la advertencia de que las importaciones absolutas están programadas para convertirse en el comportamiento "nuevo predeterminado", me encontré con este mismo problema usando Python 2.7, con o sin la __future__
directiva:
$ 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 print
declaración 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
que contiene sólo un vacío __init__.py
- y en lugar de la emisión de las importaciones de main.py
, tengo cd
'd a pkg
e importaciones ejecutar 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 __future__
directiva 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),path[0]
es la cadena vacía, que dirige a Python a buscar los módulos en el directorio actual primero.
Entonces, ¿qué me estoy perdiendo? ¿Por qué la __future__
declaración 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?
fuente
Respuestas:
El registro de cambios está redactado descuidadamente.
from __future__ import absolute_import
no le importa si algo es parte de la biblioteca estándar, yimport string
no siempre le dará el módulo de biblioteca estándar con importaciones absolutas.from __future__ import absolute_import
significa que si ustedimport string
, Python siempre buscará unstring
módulo de nivel superior , en lugar de hacerlocurrent_package.string
. Sin embargo, no afecta la lógica que Python usa para decidir qué archivo es elstring
módulo. Cuando tu lo hagaspkg/script.py
no parece parte de un paquete para Python. Siguiendo los procedimientos normales, elpkg
directorio se agrega a la ruta y todos los.py
archivos en elpkg
directorio parecen módulos de nivel superior.import string
encuentrapkg/string.py
no porque está haciendo una importación relativa, sino porquepkg/string.py
parece ser el módulo de nivel superiorstring
. El hecho de que este no es elstring
módulo de la biblioteca estándar no surge.Para ejecutar el archivo como parte del
pkg
paquete, puede hacerEn este caso, el
pkg
directorio no se agregará a la ruta. Sin embargo, el directorio actual se agregará a la ruta.También puede agregar algo de repetitivo para
pkg/script.py
que Python lo trate como parte delpkg
paquete incluso cuando se ejecuta como un archivo:Sin embargo, esto no afectará
sys.path
. Necesitará un manejo adicional para eliminar elpkg
directorio de la ruta, y sipkg
el directorio principal no está en la ruta, también deberá pegarlo en la ruta.fuente
import string
si lo sombreas accidentalmente, al menos sin rebuscarsys.modules
? ¿No es esto lo quefrom __future__ import absolute_import
se pretende prevenir? ¿Qué hace? (PD, no soy el votante negativo.)sys.path
funciona, y la pregunta real no se ha abordado en absoluto. Es decir, ¿qué hacefrom __future__ import absolute_import
realmente?sys.modules
no te dará elstring
módulo de biblioteca estándar si lo sombreaste con tu propio módulo de nivel superior.from __future__ import absolute_import
no está destinado a evitar que los módulos de nivel superior sombreen los módulos de nivel superior; se supone que evita que los módulos internos del paquete sombreen los módulos de nivel superior. Si ejecuta el archivo como parte delpkg
paquete, los archivos internos del paquete dejarán de aparecer como de nivel superior.pkg
es un paquete en la ruta de búsqueda de importación, eso debería serpython -m pkg.main
.-m
necesita un nombre de módulo, no una ruta de archivo.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:
En particular:
Tenga en cuenta que
python2 pkg/main2.py
tiene un comportamiento diferente al iniciarpython2
y luego importarpkg.main2
(que es equivalente a usar el-m
conmutador).Si alguna vez desea ejecutar un submódulo de un paquete, utilice siempre el
-m
modificador que impide que el intérprete encadene lasys.path
lista 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.
fuente
python2 pkg/main2.py
tiene un comportamiento diferente al iniciar python2 y luego importar pkg.main2?pkg/main2.py
python (versión 2) no se tratapkg
como un paquete. Si bien la utilizaciónpython2 -m pkg.main2
o la importación se haga tener en cuenta quepkg
es un paquete.