ti-enxame.com

Como usar adequadamente as importações relativas ou absolutas nos módulos Python?

O uso de importações relativas em Python tem uma desvantagem, você não poderá mais executar os módulos autônomos porque obterá uma exceção: ValueError: Attempted relative import in non-package

# /test.py: just a sample file importing foo module
import foo
...

# /foo/foo.py:
from . import bar
...
if __name__ == "__main__":
   pass

# /foo/bar.py: a submodule of foo, used by foo.py
from . import foo
...
if __name__ == "__main__":
   pass

Como devo modificar o código de amostra para poder executar todos: test.py, foo.py e bar.py

Estou procurando uma solução que funcione com python 2.6+ (incluindo 3.x).

26
sorin

Em primeiro lugar, suponho que você perceba que o que escreveu levaria a um problema de importação circular, porque foo importa bar e vice-versa; tente adicionar

from foo import bar

para test.py, e você verá que falha. O exemplo deve ser alterado para funcionar.

Portanto, o que você está pedindo é realmente voltar para a importação absoluta quando a importação relativa falhar; na verdade, se você estiver executando foo.py ou bar.py como o módulo principal, os outros módulos ficarão apenas no nível da raiz e se eles compartilharem o nome com outro módulo no sistema, de qual será escolhido depende o pedido em sys.path. Uma vez que o diretório atual é geralmente o primeiro, os módulos locais serão escolhidos se disponíveis - ou seja, se você tiver um arquivo 'os.py' no diretório de trabalho atual, ele será escolhido em vez do embutido.

Uma sugestão possível é:

foo.py

try:
    from . import bar
except ValueError:
    import bar

if __name__ == "__main__":
    pass

bar.py:

if __name__ == "__main__":
    pass

A propósito, chamar scripts da posição correta geralmente é bem melhor .

python -m foo.bar

É provavelmente o melhor caminho a percorrer. Isso executa o módulo como um script .

16
Alan Franzoni

Você pode simplesmente começar a 'executar os módulos autônomos' de uma maneira um pouco diferente:

Ao invés de:

python foo/bar.py

Usar:

python -mfoo.bar

Claro, o foo/__init__.py arquivo deve estar presente.

Observe também que você tem uma dependência circular entre foo.py e bar.py - isso não vai funcionar. Acho que é apenas um erro em seu exemplo.

Atualização: parece que também funciona perfeitamente bem usar isso como a primeira linha do foo/bar.py:

#!/usr/bin/python -mfoo.bar

Então você pode executar o script diretamente em sistemas POSIX.

22
Jacek Konieczny

Esqueça as importações relativas: você deve pensar em seu namespace de pacote como global, de qualquer maneira.

O truque para tornar isso palatável é editar sys.path adequadamente. Aqui está um pouco de pensamento:

 # um diretório acima 
 _ root_dir = os.path.dirname (os.path.dirname (os.path.realpath (__ file __))) 
 sys.path.insert (0 , _root_dir) por enquanto 
1
Edward Z. Yang

Você precisa __init__.py em cada pasta.

A importação relativa funciona apenas quando você:

python test.py

test.py importa foo.py e foo.py pode importar qualquer coisa relativa da pasta de test.py e acima.

Você não pode fazer:

cd foo
python foo.py
python bar.py

Isso nunca vai funcionar.

Você pode tentar a solução sys.path.append ou sys.path.insert, mas vai bagunçar os caminhos e terá problemas com f = open (nome do arquivo).

1
Dimitris D

Por que não simplesmente colocar o "principal" em um arquivo .py diferente?

0
Nathan Davis