¿Por qué la forma `from` de Python de una statement de importación enlaza un nombre de módulo?

Tengo un proyecto de Python con la siguiente estructura:

testapp/ ├── __init__.py ├── api │  ├── __init__.py │  └── utils.py └── utils.py 

Todos los módulos están vacíos, excepto testapp/api/__init__.py que tiene el siguiente código:

 from testapp import utils print "a", utils from testapp.api.utils import x print "b", utils 

y testapp/api/utils.py que define x :

 x = 1 

Ahora desde la raíz importo testapp.api :

 $ export PYTHONPATH=$PYTHONPATH:. $ python -c "import testapp.api" a  b  

El resultado de la importación me sorprende porque muestra que la segunda statement de import ha sobrescrito los utils . Sin embargo, los documentos indican que la instrucción from no enlazará un nombre de módulo :

La forma from no vincula el nombre del módulo: recorre la lista de identificadores, busca cada uno de ellos en el módulo que se encuentra en el paso (1) y vincula el nombre en el espacio de nombres local con el objeto encontrado.

Y de hecho, cuando en un terminal utilizo una statement from ... import ... , no se introducen nombres de módulos:

 >>> from os.path import abspath >>> path Traceback (most recent call last): File "", line 1, in  NameError: name 'path' is not defined 

Sospecho que esto tiene que ver con Python, en el momento de la segunda statement de importación, tratando de importar testapp.api.utils que se refiere a testapp.utils y falla, pero no estoy seguro.

¿Que está sucediendo aquí?

Desde la documentación del sistema de importación :

Cuando se carga un submódulo utilizando cualquier mecanismo (por ejemplo, las API de import , las declaraciones de import o import-from , o el __import__() ), se coloca un enlace en el espacio de nombres del módulo principal al objeto del submódulo. Por ejemplo, si el paquete de spam tiene un foo submódulo, después de importar spam.foo , el spam no spam tendrá un atributo foo que está vinculado al submódulo. Supongamos que tiene la siguiente estructura de directorios:

 spam/ __init__.py foo.py bar.py 

y spam/__init__.py tiene las siguientes líneas:

 from .foo import Foo from .bar import Bar 

luego, al ejecutar lo siguiente se coloca un enlace de nombre a foo y bar en el módulo de spam :

 >>> import spam >>> spam.foo  >>> spam.bar  

Dadas las reglas de enlace de nombres familiares de Python, esto puede parecer sorprendente, pero en realidad es una característica fundamental del sistema de importación. Lo invariable es que si tiene sys.modules['spam'] y sys.modules['spam.foo'] (como lo haría después de la importación anterior), este último debe aparecer como el atributo foo del primero.

Si lo hace from testapp.api.utils import x , la statement de importación no cargará utils en el espacio de nombres local. Sin embargo, la maquinaria de importación cargará utils en el espacio de nombres testapp.api , para que las importaciones posteriores funcionen correctamente. Simplemente sucede que en su caso, testapp.api es también el espacio de nombres local, por lo que está recibiendo una sorpresa.