ti-enxame.com

Como fazer importações relativas em Python?

Imagine esta estrutura de diretórios:

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

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

Eu tentei from ..sub2 import mod2, mas estou recebendo uma "tentativa de importação relativa em não-pacote".

Eu pesquisei por aí, mas encontrei apenas hacks "sys.path handling". Não há um caminho limpo?


Edit: todos os meus __init__.py estão atualmente vazios

Edit2: Estou tentando fazer isso porque sub2 contém classes que são compartilhadas entre subpacotes (sub1, subX, etc.).

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

487
Joril

Todo mundo parece querer dizer o que você deveria estar fazendo, em vez de apenas responder à pergunta.

O problema é que você está executando o módulo como '__main__' passando o mod1.py como um argumento para o interpretador.

De PEP 328 :

As importações relativas usam o atributo __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 de pacote (por exemplo, ele está configurado 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 está realmente localizado no sistema de arquivos.

No Python 2.6, eles estão adicionando a capacidade de fazer referência a 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.

306
John B
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. Você executa python main.py.
  2. main.py faz: import app.package_a.module_a
  3. module_a.py faz import app.package_b.module_b

Como alternativa, 2 ou 3 podem usar: from app.package_a import module_a

Isso funcionará contanto que você tenha app no seu PYTHONPATH. main.py pode estar em qualquer lugar.

Portanto, você escreve um setup.py para copiar (instalar) todo o pacote de aplicativos e subpacotes para as pastas python do sistema de destino e main.py para as pastas de script do sistema de destino.

116
nosklo

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

Eu faço as importações relativas como from ..sub2 import mod2 e, em seguida, se eu quiser executar mod1.py, então eu vou para o diretório pai de app e execute o módulo usando o switch python -m como python -m app.sub1.mod1.

A verdadeira razão pela qual esse problema ocorre com importações relativas é que as importações relativas funcionam tomando a propriedade __name__ do módulo. Se o módulo estiver sendo executado diretamente, __name__ é definido como __main__ e não contém informações sobre a estrutura do pacote. E é por isso que o python reclama do erro relative import in non-package.

Assim, usando o switch -m, você fornece as informações da estrutura do pacote para python, através das quais ele pode resolver as importações relativas com sucesso.

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

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

114
Pankaj

"Guido vê rodando scripts dentro de um pacote como anti-padrão" (rejected PEP-3122 )

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

46
lesnik

Isso é resolvido 100%:

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

Importar configurações/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')
27
Роман Арсеньев
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 este trecho para importar módulos de caminhos, espero que isso ajude

24
iElectric

explicação de nosklo's resposta com exemplos

nota: todos os arquivos __init__.py 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ê executar $ python main.py, ele retornará:

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_b arquivo usado na pasta package_a, que é o que você quer. Certo??

21
suhailvs

Este é infelizmente um hack sys.path, mas funciona muito bem.

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

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

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


import mymodule.mymodule1.mymodule1_1  

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

e gostaria de obter 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 __== '__main__':
    sys.path.insert(0, '../..')

Então meio que um hack, mas tudo funcionou! Então tenha em mente, se você quiser que sua decisão seja substituir outros caminhos então você precisa usar sys.path.insert (0, pathname) para fazê-lo funcionar! Este foi um ponto de atrito muito frustrante para mim, muitas pessoas dizem usar a função "append" para sys.path, mas isso não funciona se você já tem um módulo definido (eu acho isso um comportamento muito estranho)

11
Garrett Berg

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 diretório scripts.

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

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

import imp

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

Isto é retirado de isto SO resposta .

8
LondonRob

Dê uma olhada em http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports . Você poderia fazer

from .mod1 import stuff
4
mossplix

De Python doc ,

No Python 2.5, você pode alterar o comportamento da importação para importações absolutas usando uma diretiva from __future__ import absolute_import. Esse comportamento de importação absoluta se tornará o padrão em uma versão futura (provavelmente Python 2.7). Assim que as importações absolutas forem o padrão, import string sempre 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 string em seu código

2
jung rhew

Além do que John B disse, parece que a configuração da variável __package__ deve ajudar, em vez de alterar __main__ que pode estragar outras coisas. Mas até onde eu 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 em sys.path, até onde eu possa entender.

Também devo mencionar que não encontrei como formatar a string que deveria entrar nessas variáveis. É "package_head.subfolder.module_name" ou o que?

1
Gabriel

Eu achei que é mais fácil definir a variável de ambiente "PYTHONPATH" para a pasta principal:

bash$ export PYTHONPATH=/PATH/TO/APP

então:

import sub1.func1
#...more import

claro, PYTHONPATH é "global", mas não trouxe problemas para mim ainda.

1
Andrew_1510