¿Qué hace desde __future__ importar absolute_import en realidad?

He respondido una pregunta sobre las importaciones absolutas en Python, que pensé que entendía al leer el registro de cambios de Python 2.5 y el PEP que lo acompaña. Sin embargo, al instalar Python 2.5 e intentar crear un ejemplo de uso from __future__ import absolute_import , me doy cuenta de que las cosas no son tan claras.

Directamente del registro de cambios vinculado anteriormente, esta statement resumió con precisión mi comprensión del cambio de importación absoluta:

Digamos que tienes 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ódulos pkg.main y pkg.string .

Considere el código en el módulo main.py. ¿Qué pasa si ejecuta la import string la statement? En Python 2.4 y 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ódulo pkg.string , y ese módulo está vinculado al nombre "string" en el espacio de nombres del módulo pkg.main .

Así que creé esta estructura de directorio exacta:

 $ ls -R .: pkg/ ./pkg: __init__.py main.py string.py 

__init__.py y string.py están vacíos. main.py contiene el siguiente código:

 import string print string.ascii_uppercase 

Como era de esperar, 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  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 las importaciones 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 el valor predeterminado, la import string siempre encontrará la versión de la biblioteca estándar.

Así que creé pkg/main2.py , idéntico a 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 

Sin embargo, ejecutar esto con Python 2.5 … falla con un AttributeError :

 $ python2.5 pkg/main2.py Traceback (most recent call last): File "pkg/main2.py", line 3, in  print string.ascii_uppercase AttributeError: 'module' object has no attribute 'ascii_uppercase' 

Esto contradice bastante la afirmación de que la import string siempre encontrará la versión std-lib con las importaciones absolutas habilitadas. Más aún, a pesar de la advertencia de que las importaciones absolutas están progtwigdas para convertirse en el comportamiento “nuevo predeterminado”, tengo 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  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  print string.ascii_uppercase AttributeError: 'module' object has no attribute 'ascii_uppercase' 

así como Python 3.5, con o sin (asumiendo que la statement de print se modifique en ambos archivos):

 $ python3.5 pkg/main.py Traceback (most recent call last): File "pkg/main.py", line 2, in  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  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 statement permanece sin cambios en los mismos documentos para Python 3):

sys.path

(…)

Como se inicializó al iniciar el progtwig, el primer elemento de esta lista, path[0] , es el directorio que contiene el script que se usó para invocar al intérprete de Python. Si el directorio del script no está disponible (por ejemplo, si el intérprete se invoca interactivamente o si el script se lee desde la entrada estándar), la path[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 statement __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?

Related of "¿Qué hace desde __future__ importar absolute_import en realidad?"

El registro de cambios está redactado de forma descuidada. from __future__ import absolute_import no le importa si algo es parte de la biblioteca estándar, y la 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 ser 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é realizando una importación relativa, sino porque pkg/string.py parece ser la string módulo de nivel superior. El hecho de que esto no sea el módulo de string biblioteca estándar no surge.

Para ejecutar el archivo como parte del paquete pkg , puedes 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 de boilerplate 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á algo de manejo adicional para eliminar el directorio pkg de la ruta, y si el directorio principal 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 "", line 1, in  File "pkg/main1.py", line 1, in  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  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 (lo que equivale a usar el pkg.main2 -m ).

Si alguna vez desea ejecutar un submódulo de un paquete, use siempre el sys.path -m que impide 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 los submódulos de paquetes, ya que proporcionan más semántica y mejores mensajes de error en caso de falla.