Tenho respondido uma pergunta sobre as importações absolutos em Python, que eu pensei que eu entendi com base na leitura do changelog Python 2.5 e acompanhando PEP . No entanto, ao instalar o Python 2.5 e tentar criar um exemplo de uso adequado from __future__ import absolute_import
, percebo que as coisas não são tão claras.
Diretamente do changelog acima, esta declaração resumiu com precisão minha compreensão da mudança absoluta de importação:
Digamos que você tenha um diretório de pacotes como este:
pkg/ pkg/__init__.py pkg/main.py pkg/string.py
Isso define um pacote chamado
pkg
contendo os subpkg.main
-pkg.string
módulos e .Considere o código no módulo main.py. O que acontece se a instrução for executada
import string
? No Python 2.4 e versões anteriores, ele procurará primeiro no diretório do pacote uma importação relativa, localiza pkg / string.py, importa o conteúdo desse arquivo comopkg.string
módulo e esse módulo é vinculado ao nome"string"
nopkg.main
espaço de nome do módulo.
Então, eu criei essa estrutura de diretório exata:
$ ls -R
.:
pkg/
./pkg:
__init__.py main.py string.py
__init__.py
e string.py
estão vazios. main.py
contém o seguinte código:
import string
print string.ascii_uppercase
Como esperado, executar isso com o Python 2.5 falha com um 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'
No entanto, mais adiante no changelog 2.5, encontramos o seguinte (ênfase adicionada):
No Python 2.5, você pode mudar
import
o comportamento para importações absolutas usando umafrom __future__ import absolute_import
diretiva. Esse comportamento de importação absoluta se tornará o padrão em uma versão futura (provavelmente Python 2.7). Uma vez que as importações absolutas são o padrão,import string
sempre encontrará a versão da biblioteca padrão.
Criei assim pkg/main2.py
, idêntico a, main.py
mas com a futura diretiva de importação adicional. Agora, fica assim:
from __future__ import absolute_import
import string
print string.ascii_uppercase
Executar isso com o Python 2.5, no entanto ... falha com um 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'
Este bastante categoricamente contradiz a afirmação de que import string
irá sempre encontrar a versão std-lib com as importações absolutos habilitado. Além do mais, apesar do aviso de que importações absolutas estão agendadas para se tornar o comportamento "novo padrão", encontrei o mesmo problema usando o Python 2.7, com ou sem a __future__
diretiva:
$ 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'
bem como Python 3.5, com ou sem (assumindo que a print
instrução foi alterada nos dois arquivos):
$ 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'
Eu testei outras variações disso. Em vez de string.py
, eu criei um módulo vazio - um diretório chamado string
contendo apenas um vazio __init__.py
- e em vez de emitir as importações a partir main.py
, eu tenho cd
'd para pkg
e executar as importações diretamente do REPL. Nenhuma dessas variações (nem uma combinação delas) alterou os resultados acima. Não consigo conciliar isso com o que li sobre a __future__
diretiva e as importações absolutas.
Parece-me que isso é facilmente explicável pelo seguinte (isso é dos documentos do Python 2, mas essa declaração permanece inalterada nos mesmos documentos do Python 3):
sys.path
(...)
Conforme inicializado na inicialização do programa, o primeiro item desta lista
path[0]
, é o diretório que contém o script usado para chamar o interpretador Python. Se o diretório do script não estiver disponível (por exemplo, se o intérprete for chamado interativamente ou se o script for lido a partir da entrada padrão),path[0]
será a string vazia, que instrui o Python a pesquisar os módulos no diretório atual primeiro.
Então o que estou perdendo? Por que a __future__
declaração aparentemente não faz o que diz e qual é a resolução dessa contradição entre essas duas seções da documentação, bem como entre o comportamento descrito e o real?
fonte
Respostas:
O changelog é redigido de forma desleixada.
from __future__ import absolute_import
não se importa se algo faz parte da biblioteca padrão eimport string
nem sempre fornece o módulo da biblioteca padrão com importações absolutas.from __future__ import absolute_import
significa que, se vocêimport string
, o Python sempre procurará umstring
módulo de nível superior , em vez decurrent_package.string
. No entanto, isso não afeta a lógica que o Python usa para decidir qual arquivo é ostring
módulo. Quando você fazpkg/script.py
não parece parte de um pacote para o Python. Seguindo os procedimentos normais, opkg
diretório é adicionado ao caminho e todos os.py
arquivos nopkg
diretório se parecem com módulos de nível superior.import string
encontrapkg/string.py
não porque está fazendo uma importação relativa, mas porquepkg/string.py
parece ser o módulo de nível superiorstring
. O fato de esse não ser ostring
módulo da biblioteca padrão não aparece.Para executar o arquivo como parte do
pkg
pacote, você pode fazerNesse caso, o
pkg
diretório não será adicionado ao caminho. No entanto, o diretório atual será adicionado ao caminho.Você também pode adicionar alguns clichês para
pkg/script.py
fazer o Python tratá-lo como parte dopkg
pacote, mesmo quando executado como um arquivo:No entanto, isso não afetará
sys.path
. Você precisará de um tratamento adicional para remover opkg
diretório do caminho, e sepkg
o diretório pai não estiver no caminho, você também precisará colocá-lo no caminho.fonte
import string
se acidentalmente ocultá-lo, pelo menos sem passar por issosys.modules
. Não é isso quefrom __future__ import absolute_import
se pretende impedir? O que isso faz? (PS, eu não sou o derrotador).sys.path
funciona, e a pergunta real ainda não foi abordada. Ou seja, o quefrom __future__ import absolute_import
realmente faz?sys.modules
não é ostring
módulo da biblioteca padrão se você o sombrear com seu próprio módulo de nível superior.from __future__ import absolute_import
não pretende impedir que os módulos de nível superior sombream os módulos de nível superior; deve parar os módulos internos do pacote de sombrear os módulos de nível superior. Se você executar o arquivo como parte dopkg
pacote, os arquivos internos do pacote param de aparecer como de nível superior.pkg
é um pacote no caminho de pesquisa de importação, deveria serpython -m pkg.main
.-m
precisa de um nome de módulo, não de um caminho de arquivo.A diferença entre importações absolutas e relativas entra em jogo somente quando você importa um módulo de um pacote e esse módulo importa um outro submódulo desse pacote. Veja a diferença:
Em particular:
Observe que o
python2 pkg/main2.py
comportamento é diferente ao iniciarpython2
e depois importarpkg.main2
(o que equivale a usar a-m
opção).Se você quiser executar um submódulo de um pacote, use sempre a
-m
opção que impede o intérprete de encadear asys.path
lista e manipula corretamente a semântica do submódulo.Além disso, prefiro usar importações relativas explícitas para sub-módulos de pacotes, pois fornecem mais semântica e melhores mensagens de erro em caso de falha.
fonte
python2 pkg/main2.py
tem um comportamento diferente ao iniciar o python2 e depois importar o pkg.main2?pkg/main2.py
python (versão 2) não tratapkg
como um pacote. Enquanto estiver usandopython2 -m pkg.main2
ou importá-lo fazer levar em conta quepkg
é um pacote.