Como fazer importações relativas em Python?

527

Imagine esta estrutura de diretório:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

Estou codificando mod1e preciso importar algo de mod2. Como devo fazer isso?

Eu tentei, from ..sub2 import mod2mas estou recebendo uma "Tentativa de importação relativa no pacote".

Eu procurei no Google, mas encontrei apenas sys.pathhacks de "manipulação". Não existe um caminho limpo?


Editar: todos os meus __init__.pyestão vazios no momento

Edit2: Eu estou tentando fazer isso porque sub2 contém classes que são compartilhados entre pacotes de sub ( sub1, subX, etc.).

Edit3: O comportamento que estou procurando é o mesmo descrito no PEP 366 (obrigado John B)

Joril
fonte
8
Eu recomendo atualizar a sua pergunta para torná-lo mais claro que você está descrevendo o problema abordado no PEP 366.
John B
2
É uma explicação longa, mas verifique aqui: stackoverflow.com/a/10713254/1267156 Respondi a uma pergunta muito semelhante. Eu tive esse mesmo problema até a noite passada.
Sevvy325
3
Para aqueles que desejam carregar um módulo localizado em um caminho arbitrário, consulte este: stackoverflow.com/questions/67631/...
Evgeni Sergeev
2
Em uma nota relacionada, o Python 3 alterará o tratamento padrão das importações para ser absoluto por padrão; importações relativas deverão ser especificadas explicitamente.
Ross

Respostas:

337

Todo mundo parece querer lhe dizer o que você deve fazer, em vez de apenas responder à pergunta.

O problema é que você está executando o módulo como '__main__' passando o mod1.py como argumento ao intérprete.

Do PEP 328 :

As importações relativas usam o atributo __name__ de um módulo para determinar a posição desse módulo na hierarquia de pacotes. Se o nome do módulo não contiver nenhuma informação do pacote (por exemplo, está definido como '__main__'), as importações relativas serão resolvidas como se o módulo fosse um módulo de nível superior, independentemente de onde o módulo esteja realmente localizado no sistema de arquivos.

No Python 2.6, eles estão adicionando a capacidade de referenciar módulos em relação ao módulo principal. PEP 366 descreve a mudança.

Atualização : De acordo com Nick Coghlan, a alternativa recomendada é executar o módulo dentro do pacote usando a opção -m.

John B
fonte
2
A resposta aqui envolve mexer com sys.path em todos os pontos de entrada do seu programa. Eu acho que é a única maneira de fazer isso.
Nick Retallack
76
A alternativa recomendada é executar módulos dentro de pacotes usando o -mswitch, em vez de especificar diretamente o nome do arquivo.
Ncoghlan
127
Eu não entendo: onde está a resposta aqui? Como se pode importar módulos em uma estrutura de diretórios?
Tom
27
@ Tom: Neste caso, mod1 faria from sub2 import mod2. Em seguida, para executar o mod1, de dentro do aplicativo, faça python -m sub1.mod1.
Xiong Chiamiov
11
@XiongChiamiov: isso significa que você não pode fazê-lo se o seu python estiver incorporado em um aplicativo, para que você não tenha acesso às opções de linha de comando do python?
precisa saber é
130

Aqui está a solução que funciona para mim:

Faço as importações relativas como from ..sub2 import mod2 e, se quiser executar mod1.py, vou para o diretório pai appe executo o módulo usando a opção python -m como python -m app.sub1.mod1.

A verdadeira razão pela qual esse problema ocorre com as importações relativas é que as importações relativas funcionam ao obter a __name__propriedade do módulo. Se o módulo estiver sendo executado diretamente, ele __name__será definido como __main__e não conterá nenhuma informação sobre a estrutura do pacote. E é por isso que o python reclama do relative import in non-packageerro.

Portanto, usando a opção -m, você fornece as informações da estrutura do pacote ao python, através da qual ele pode resolver as importações relativas com êxito.

Eu encontrei esse problema várias vezes ao fazer importações relativas. E, depois de ler todas as respostas anteriores, ainda não consegui descobrir como resolvê-lo, de uma maneira limpa, sem precisar inserir o código padrão em todos os arquivos. (Embora alguns comentários tenham sido realmente úteis, obrigado a @ncoghlan e @XiongChiamiov)

Espero que isso ajude alguém que está lutando com um problema relativo de importações, porque passar pelo PEP não é realmente divertido.

Pankaj
fonte
9
Melhor resposta IMHO: não apenas explica por que o OP teve o problema, mas também encontra uma maneira de resolvê-lo sem alterar a maneira como seus módulos importam . Afinal, as importações relativas do OP foram boas. O culpado foi a falta de acesso a pacotes externos ao executar diretamente como script, algo que -mfoi projetado para resolver.
precisa saber é o seguinte
26
Observe também: essa resposta foi 5 anos após a pergunta. Esses recursos não estavam disponíveis no momento.
JeremyKun
1
Se você deseja importar um módulo do mesmo diretório, pode fazê-lo from . import some_module.
Rotareti
124
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. Você corre python main.py.
  2. main.py faz: import app.package_a.module_a
  3. module_a.py faz import app.package_b.module_b

Alternativamente 2 ou 3 podem usar: from app.package_a import module_a

Isso funcionará enquanto você tiver appno seu PITONONA. main.pypoderia estar em qualquer lugar então.

Então, você escreve um setup.pypara copiar (instalar) todo o pacote de aplicativos e subpacotes nas pastas python do sistema de main.pydestino e nas pastas de script do sistema de destino.

nosklo
fonte
3
Excelente resposta. Existe alguma maneira de importar dessa maneira sem instalar o pacote no PYTHONPATH?
auraham
4
Leitura adicional sugerida: blog.habnab.it/blog/2013/07/21/python-packages-and-you
nosklo 17/10/2013
6
então, um dia, será necessário alterar o nome do aplicativo para test_app. o que aconteceria? Você precisará alterar todos os códigos-fonte, importar app.package_b.module_b -> test_app.package_b.module_b. isso é uma prática absolutamente ruim ... E devemos tentar usar a importação relativa dentro do pacote.
Spybdai
49

"O Guido vê os scripts em execução em um pacote como um antipadrão" ( PEP-3122 rejeitado )

Passei tanto tempo tentando encontrar uma solução, lendo postagens relacionadas aqui no Stack Overflow e dizendo para mim mesmo "deve haver uma maneira melhor!". Parece que não há.

lesnik
fonte
10
Nota: O pep-366 já mencionado (criado na mesma época que o pep-3122 ) fornece os mesmos recursos, mas usa uma implementação compatível com versões anteriores, ou seja, se você deseja executar um módulo dentro de um pacote como um script e usar importações relativas explícitas nele, você pode executá-lo usando -mswitch: python -m app.sub1.mod1ou invocar a app.sub1.mod1.main()partir de um script de nível superior (por exemplo, gerado a partir dos pontos de entrada dos setuptools definidos em setup.py).
JFS
+1 para usar ferramentas de instalação e pontos de entrada - é uma maneira adequada de configurar scripts que serão executados do lado de fora, em um local bem definido, em oposição aos hackers infinitamente
PYTHONPATH
38

Isso é resolvido 100%:

  • aplicativo/
    • main.py
  • definições/
    • local_setings.py

Importe settings / local_setting.py em app / main.py:

main.py:

import sys
sys.path.insert(0, "../settings")


try:
    from local_settings import *
except ImportError:
    print('No Import')
Роман Арсеньев
fonte
2
obrigado! todas as pessoas estavam me forçando a executar meu script de maneira diferente em vez de me dizer como resolvê-lo no script. Mas eu tive que mudar o código para usar sys.path.insert(0, "../settings")e depoisfrom local_settings import *
Vit Bernatik
25
def import_path(fullpath):
    """ 
    Import a file with full path specification. Allows one to
    import from anywhere, something __import__ does not do. 
    """
    path, filename = os.path.split(fullpath)
    filename, ext = os.path.splitext(filename)
    sys.path.append(path)
    module = __import__(filename)
    reload(module) # Might be out of date
    del sys.path[-1]
    return module

Estou usando esse trecho para importar módulos de caminhos, espero que ajude

iElectric
fonte
2
Estou usando esse trecho, combinado com o módulo imp (como explicado aqui [1]) com grande efeito. [1]: stackoverflow.com/questions/1096216/…
Xiong Chiamiov
7
Provavelmente, sys.path.append (caminho) deve ser substituído por sys.path.insert (0, caminho) e sys.path [-1] deve ser substituído por sys.path [0]. Caso contrário, a função importará o módulo errado, se já houver um módulo com o mesmo nome no caminho de pesquisa. Por exemplo, se houver "some.py" no diretório atual, import_path ("/ imports / some.py") importará o arquivo errado.
Alex Che
Concordo! Às vezes, outras importações relativas terão precedência. Use sys.path.insert
iElectric
Como você replicaria o comportamento de de x import y (ou *)?
Leveque
Não está claro, especifique o uso completo desse script para resolver o problema do OP.
Mrgloom # 24/18
21

explicação da nosklo'sresposta com exemplos

nota: todos os __init__.pyarquivos estão vazios.

main.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       fun_a.py
    package_b/ ->
       __init__.py
       fun_b.py

app / package_a / fun_a.py

def print_a():
    print 'This is a function in dir package_a'

app / package_b / fun_b.py

from app.package_a.fun_a import print_a
def print_b():
    print 'This is a function in dir package_b'
    print 'going to call a function in dir package_a'
    print '-'*30
    print_a()

main.py

from app.package_b import fun_b
fun_b.print_b()

se você executá- $ python main.pylo, retorna:

This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
  • main.py faz: from app.package_b import fun_b
  • fun_b.py faz from app.package_a.fun_a import print_a

então arquivo na pasta package_busado arquivo na pasta package_a, que é o que você deseja. Direita??

suhailvs
fonte
12

Infelizmente, este é um hack do sys.path, mas funciona muito bem.

Encontrei esse problema com outra camada: eu já tinha um módulo com o nome especificado, mas era o módulo errado.

o que eu queria fazer era o seguinte (o módulo em que eu estava trabalhando era o module3):

mymodule\
   __init__.py
   mymodule1\
      __init__.py
      mymodule1_1
   mymodule2\
      __init__.py
      mymodule2_1


import mymodule.mymodule1.mymodule1_1  

Observe que eu já instalei o mymodule, mas na minha instalação não tenho "mymodule1"

e eu receberia um ImportError porque estava tentando importar dos meus módulos instalados.

Eu tentei fazer um sys.path.append, e isso não funcionou. O que funcionou foi um sys.path.insert

if __name__ == '__main__':
    sys.path.insert(0, '../..')

É uma espécie de hack, mas conseguiu que tudo funcionasse! Portanto, lembre-se, se você deseja que sua decisão substitua outros caminhos , use sys.path.insert (0, caminho) para fazê-lo funcionar! Este foi um ponto de discórdia muito frustrante para mim, muitas pessoas dizem usar a função "anexar" ao sys.path, mas isso não funciona se você já tiver um módulo definido (acho um comportamento muito estranho)

Garrett Berg
fonte
sys.path.append('../')funciona bem para mim (Python 3.5.2)
Nister 05/11
Eu acho que isso é bom, pois localiza o hack no executável e não afeta outros módulos que podem depender dos seus pacotes.
Tom Russell
10

Deixe-me colocar isso aqui para minha própria referência. Eu sei que não é um bom código Python, mas eu precisava de um script para um projeto em que estava trabalhando e queria colocar o script em um scriptsdiretório.

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
milkypostman
fonte
9

Como o @EvgeniSergeev diz nos comentários ao OP, você pode importar o código de um .pyarquivo em um local arbitrário com:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

Isso é retirado desta resposta SO .

LondonRob
fonte
2

No documento Python ,

No Python 2.5, você pode mudar o comportamento da importação para importações absolutas usando uma from __future__ import absolute_importdiretiva. 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 stringsempre encontrará a versão da biblioteca padrão. É sugerido que os usuários comecem a usar importações absolutas o máximo possível, por isso é preferível começar a escrever from pkg import stringno seu código

jung rhew
fonte
1

Achei mais fácil definir a variável de ambiente "PYTHONPATH" para a pasta superior:

bash$ export PYTHONPATH=/PATH/TO/APP

então:

import sub1.func1
#...more import

é claro, PYTHONPATH é "global", mas ainda não me causou problemas.

Andrew_1510
fonte
É basicamente assim que virtualenvpermite gerenciar suas instruções de importação.
Byxor
1

Além do que John B disse, parece que definir a __package__variável deve ajudar, em vez de mudar o __main__que pode estragar outras coisas. Mas, tanto quanto pude testar, não funciona completamente como deveria.

Eu tenho o mesmo problema e nem o PEP 328 nem o 366 resolvem o problema completamente, pois ambos, no final do dia, precisam que o cabeçalho do pacote seja incluído sys.path, tanto quanto eu possa entender.

Também devo mencionar que não encontrei como formatar a string que deve ser inserida nessas variáveis. É "package_head.subfolder.module_name"ou o quê?

Gabriel
fonte
0

Você precisa anexar o caminho do módulo para PYTHONPATH:

export PYTHONPATH="${PYTHONPATH}:/path/to/your/module/"
Giorgos Myrianthous
fonte
1
Isto é aproximadamente o mesmo que manipular sys.path, já que sys.pathé inicializado a partir dePYTHONPATH
Joril 25/03
@Joril Isso está correto, mas sys.pathprecisa ser codificado no código fonte, ao contrário do PYTHONPATHque é uma variável de ambiente e pode ser exportado.
Giorgos Myrianthous 25/03