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.