Importar de um caminho relativo em Python

104

Eu tenho uma pasta para meu código de cliente, uma pasta para meu código de servidor e uma pasta para o código que é compartilhado entre eles

Proj/
    Client/
        Client.py
    Server/
        Server.py
    Common/
        __init__.py
        Common.py

Como importo Common.py de Server.py e Client.py?

Desenhou
fonte
Relacionado: stackoverflow.com/q/72852/1025391
moooeeeep

Respostas:

140

EDITAR novembro de 2014 (3 anos depois):

Python 2.6 e 3.x oferece suporte a importações relativas adequadas, onde você pode evitar fazer qualquer coisa hackeada. Com este método, você sabe que está obtendo uma importação relativa em vez de uma importação absoluta . O '..' significa ir para o diretório acima de mim:

from ..Common import Common

Como uma advertência, isso só funcionará se você executar seu python como um módulo, de fora do pacote. Por exemplo:

python -m Proj

Maneira hacky original

Este método ainda é comumente usado em algumas situações, onde você nem mesmo "instala" seu pacote. Por exemplo, é popular entre os usuários do Django.

Você pode adicionar Common / ao seu sys.path (a lista de caminhos que o Python procura para importar coisas):

import sys, os
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'Common'))
import Common

os.path.dirname(__file__) apenas fornece o diretório em que seu arquivo Python atual está, e então navegamos para 'Comum /' o diretório e importamos 'Comum' o módulo.

Dave
fonte
2
Não modifique o caminho dos módulos Python manualmente, pode ser apenas para hacks rápidos. Aprender o gerenciamento de pacotes Python usando distutils, ferramentas de configuração, etc. é geralmente uma habilidade necessária para resolver problemas como esse.
Sascha Gottfried
1
@SaschaGottfried concorda totalmente, embora se você não estiver fazendo um pacote distribuível, provavelmente não fará diferença. Por exemplo, no Django você nunca realmente instala seu aplicativo com distutils, então o método acima é um hack fácil. Mas de qualquer forma eu editei a resposta com o que faria hoje em dia.
Dave
32
Obrigado por responder a pergunta real em vez de pregar sobre a técnica adequada. Existem muitas boas razões para fazer importações relativas.
musaranho
como você subiria mais de um nível?
jxramos
10
para subir mais um nível, use um ponto adicional para cada nível. @jxramos ex: from ...myfilevai para../../myfile
WattsInABox
10

Engraçado, o mesmo problema que acabei de conhecer, e eu consigo este trabalho da seguinte maneira:

combinando com o comando linux ln, podemos tornar as coisas muito mais simples:

1. cd Proj/Client
2. ln -s ../Common ./

3. cd Proj/Server
4. ln -s ../Common ./

E, agora, se você deseja importar some_stuffdo arquivo: Proj/Common/Common.pypara o seu arquivo Proj/Client/Client.py:, assim:

# in Proj/Client/Client.py
from Common.Common import some_stuff

E, o mesmo se aplica a Proj/Server, Também funciona para o setup.pyprocesso, uma mesma questão discutida aqui , espero que ajude!

jacoolee
fonte
10

Não faça importação relativa.

Do PEP8 :

Importações relativas para importações intra-embalagem são altamente desencorajadas.

Coloque todo o seu código em um super pacote (ou seja, "myapp") e use subpacotes para cliente, servidor e código comum.

Atualização: " Python 2.6 e 3.x suporta importações relativas adequadas (...) ". Veja as respostas de Dave para mais detalhes.

Michał Šrajer
fonte
1
Imagine que você adiciona algum código ao final do cliente e do servidor após a if __name__ == "__main__":linha ' '. Ou seja, você deseja poder usá-los como scripts independentes. Como fazer isso corretamente? Acho que é um caso de uso perfeitamente comum que deve ser suportado. Por que isso está desanimado?
Jabba
83
Estou surpreso que "não faça isso" é a resposta aceita para um "como é que eu ..." questão (bem, exceto para Rails <g>.) Não são razões ocasionais de fazer isso. Eu uso uma solução semelhante à que Dave sugere.
Tom Wilson
1
@TomWilson: Não é uma resposta pura do tipo "não faça isso". Há "faça assim" abaixo.
Michał Šrajer
2
Alguém deveria contar para os caras da Numpy! Eles usam uma tonelada de importações relativas!
Austin A
12
Esta resposta não se aplica às versões atuais do Python. A parte citada não é mais encontrada no PEP 8. Hoje em dia, diz-se: "importações relativas explícitas são uma alternativa aceitável para importações absolutas, especialmente quando se trata de layouts de pacotes complexos onde usar importações absolutas seria desnecessariamente prolixo"
moooeeeep
8

Fazer uma importação relativa é absolutamente OK! Aqui está o que o meu pequeno faz:

#first change the cwd to the script path
scriptPath = os.path.realpath(os.path.dirname(sys.argv[0]))
os.chdir(scriptPath)

#append the relative location you want to import from
sys.path.append("../common")

#import your module stored in '../common'
import common.py
Gary Beardsley
fonte
1
Mas é melhor você saber para onde sys.argv [0] está realmente apontando - (provavelmente) não é o diretório em que você estava quando iniciou o python.
CarlH
Este é um hack rápido, com muitas armadilhas. Mas a pergunta não era ainda melhor.
Sascha Gottfried
1
Isso está escrito claramente, mas o hack original na resposta de Dave é melhor porque usa __file__para obter a relação adequada do arquivo atual
John Neuhaus
4

O método de importação padrão já é "relativo", a partir do PYTHONPATH. O PYTHONPATH é por padrão, para algumas bibliotecas do sistema junto com a pasta do arquivo de origem original. Se você executar com -m para executar um módulo, o diretório atual será adicionado ao PYTHONPATH. Portanto, se o ponto de entrada do seu programa estiver dentro do Proj, useimport Common.Common deve funcionar dentro de Server.py e Client.py.

Não faça uma importação relativa. Não funcionará como você deseja.

Jonathan Sternberg
fonte
1
Se isso for verdade, por que as principais respostas não dizem isso? Isso vai funcionar ou não?
Anônimo