Alguém pode explicar as importações relativas do python?


174

Durante toda a minha vida, não consigo fazer com que as importações relativas do python funcionem. Eu criei um exemplo simples de onde ele não funciona:

A estrutura de diretórios é:

/__init__.py
/start.py
/parent.py
/sub/__init__.py
/sub/relative.py

/start.py contém apenas: import sub.relative

/sub/relative.py contém apenas from .. import parent

Todos os outros arquivos estão em branco.

Ao executar o seguinte na linha de comandos:

$ cd /
$ python start.py

Eu recebo:

Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import sub.relative
  File "/home/cvondrick/sandbox/sub/relative.py", line 1, in <module>
    from .. import parent
ValueError: Attempted relative import beyond toplevel package

Estou usando o Python 2.6. Por que esse é o caso? Como faço para que este exemplo de sandbox funcione?

Respostas:


140

Você está importando do pacote "sub". start.pynão está em um pacote, mesmo que haja um __init__.pypresente.

Você precisaria iniciar seu programa em um diretório parent.py:

./start.py

./pkg/__init__.py
./pkg/parent.py
./pkg/sub/__init__.py
./pkg/sub/relative.py

Com start.py:

import pkg.sub.relative

Agora o pkg é o pacote de nível superior e sua importação relativa deve funcionar.


Se você deseja manter seu layout atual, basta usar import parent. Como você usa start.pypara iniciar seu intérprete, o diretório onde start.pyestá localizado está no seu caminho python. parent.pyvive lá como um módulo separado.

Você também pode excluir com segurança o nível superior __init__.py, se não importar nada para um script na árvore de diretórios.


2
Você está confundindo os termos 'módulo' e 'pacote'. 'start.py' representa o módulo 'start', 'mod' e 'mod.sub' são pacotes, 'mod' é um pacote de nível superior.
Ferdinand Beyer

34
Obrigado, mas isso honestamente parece realmente bobo. Para uma linguagem tão bonita, não acredito que os designers criariam essa restrição. Não existe outra maneira?
carl

2
Não é nada bobo. As importações relativas são um meio de se referir aos módulos irmãos dentro de um pacote. Se você deseja importar um módulo de nível superior, use importações absolutas.
Ferdinand Beyer

58
Não é bobo? Portanto, no bash, não ser capaz de abordar o diretório superior relativo com ".." não incomodaria?
e-satis

2
Parece-me que a idéia do python é usar importações "absolutas" do diretório em que você iniciou o script pai. Portanto, você pode usar o caminho absoluto "importar pai" para importar o módulo pai dos irmãos. E as importações relativas algum tipo de legado ou o que quer ..
Odysseus

35

Se você deseja ligar relative.pydiretamente e, por exemplo, se realmente deseja importar de um módulo de nível superior, é necessário adicioná-lo explicitamente à sys.pathlista.
Aqui está como deve funcionar:

# Add this line to the beginning of relative.py file
import sys
sys.path.append('..')

# Now you can do imports from one directory top cause it is in the sys.path
import parent

# And even like this:
from parent import Parent

Se você acha que o exposto acima pode causar algum tipo de inconsistência, use-o:

sys.path.append(sys.path[0] + "/..")

sys.path[0] refere-se ao caminho do qual o ponto de entrada foi executado.


3

Verificando em python3:

python -V
Python 3.6.5

Exemplo 1:

.
├── parent.py
├── start.py
└── sub
    └── relative.py

- start.py
import sub.relative

- parent.py
print('Hello from parent.py')

- sub/relative.py
from .. import parent

Se rodarmos assim (apenas para garantir que PYTHONPATH esteja vazio):

PYTHONPATH='' python3 start.py

Resultado:

Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import sub.relative
  File "/python-import-examples/so-example-v1/sub/relative.py", line 1, in <module>
    from .. import parent
ValueError: attempted relative import beyond top-level package

Se alterarmos a importação em sub/relative.py

- sub/relative.py
import parent

Se rodarmos assim:

PYTHONPATH='' python3 start.py

Resultado:

Hello from parent.py

Example2:

.
├── parent.py
└── sub
    ├── relative.py
    └── start.py

- parent.py
print('Hello from parent.py')

- sub/relative.py
print('Hello from relative.py')

- sub/start.py
import relative
from .. import parent

Execute-o como:

PYTHONPATH='' python3 sub/start.py

Resultado:

Hello from relative.py
Traceback (most recent call last):
  File "sub/start.py", line 2, in <module>
    from .. import parent
ValueError: attempted relative import beyond top-level package

Se alterarmos a importação em sub/start.py:

- sub/start.py
import relative
import parent

Execute-o como:

PYTHONPATH='' python3 sub/start.py

Resultado:

Hello from relative.py
Traceback (most recent call last):
  File "sub/start.py", line 3, in <module>
    import parent
ModuleNotFoundError: No module named 'parent'

Execute-o como:

PYTHONPATH='.' python3 sub/start.py

Resultado:

Hello from relative.py
Hello from parent.py

Também é melhor usar a importação da pasta raiz, ou seja:

- sub/start.py
import sub.relative
import parent

Execute-o como:

PYTHONPATH='.' python3 sub/start.py

Resultado:

Hello from relative.py
Hello from parent.py
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.