Importar um módulo de um caminho relativo


759

Como importo um módulo Python devido ao seu caminho relativo?

Por exemplo, se dirFoocontém Foo.pye dirBar, e dirBarcontém Bar.py, como faço Bar.pypara importar Foo.py?

Aqui está uma representação visual:

dirFoo\
    Foo.py
    dirBar\
        Bar.py

Foodeseja incluir Bar, mas a reestruturação da hierarquia de pastas não é uma opção.



3
Verifique minha resposta, é a mais completa até agora, outras não estão funcionando em casos especiais, por exemplo, quando você chama o script de outro diretório ou de outro script python. Veja stackoverflow.com/questions/279237/…
sorin

Eu tive um problema semelhante e encontrei isso e funciona !! apt-get install python-profiler
ldcl289

5
Apenas no caso de alguém quiser fazê-lo estaticamente e chegar aqui (como eu fiz :) você também pode configurar a variável de ambiente PYTHONPATH
okaram

Melhor é seguir as instruções no Lib / site.py para cada caso
Avenida Gez

Respostas:


333

Supondo que os dois diretórios sejam pacotes reais do Python (o __init__.pyarquivo está dentro deles), aqui está uma solução segura para a inclusão de módulos em relação à localização do script.

Suponho que você queira fazer isso, porque você precisa incluir um conjunto de módulos com seu script. Eu uso isso na produção em vários produtos e funciona em muitos cenários especiais, como: scripts chamados de outro diretório ou executados com python execute em vez de abrir um novo intérprete.

 import os, sys, inspect
 # realpath() will make your script run, even if you symlink it :)
 cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]))
 if cmd_folder not in sys.path:
     sys.path.insert(0, cmd_folder)

 # Use this if you want to include modules from a subfolder
 cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"subfolder")))
 if cmd_subfolder not in sys.path:
     sys.path.insert(0, cmd_subfolder)

 # Info:
 # cmd_folder = os.path.dirname(os.path.abspath(__file__)) # DO NOT USE __file__ !!!
 # __file__ fails if the script is called in different ways on Windows.
 # __file__ fails if someone does os.chdir() before.
 # sys.argv[0] also fails, because it doesn't not always contains the path.

Como um bônus, essa abordagem permite forçar o Python a usar seu módulo em vez dos instalados no sistema.

Atenção! Eu realmente não sei o que está acontecendo quando o módulo atual está dentro de um eggarquivo. Provavelmente também falha.


5
posso obter uma explicação de como isso funciona? Eu tenho um problema semelhante e eu adoraria para forçar uma DIR módulo python em vez de executar uma pesquisa
carl crott

Executando o Win 7 Pro 64x e o Python 2.7, recebo alguns erros. 1) Eu tive que adicionar inspecionar à lista de importação. 2) O 1º valor, [0], na tupla é uma string vazia. O segundo, [1], mostra o nome do arquivo. Acho que o primeiro deve ser o caminho ... Alguma idéia?
Adam Lewis

2
Se você estiver indo para uma subpasta, usá-lo como este: os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]) + "/subfolder")Do NOT adicionar a subpasta antes abspath, pois isso faz com que erros graves.
Maximilian Hils

4
@ scr4ve você deve usar os.path.join () e pode adicionar o case ( cmd_subfolder) diretamente à minha resposta. obrigado!
Sorin

7
para mim realpathjá gera caminhos absolutos, portanto não preciso abspath. Também os.path.dirnamepode ser usado em vez de dividir, tornando a indexação [0]obsoleta. A linha seria então:os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
ted

328

Certifique-se de que o dirBar tenha o __init__.pyarquivo - isso transforma um diretório em um pacote Python.


204
Observe que esse arquivo pode estar completamente vazio.
Harley Holcombe

47
Se o diretório pai do dirBar não estiver sys.path, a presença de __init__.pyno dirBardiretório não ajudará muito.
JFS

7
-1, a adição __init.py__funcionará apenas quando o diretório já estiver no sys.path e, no meu caso, não estava. Solução por "sorin" (aceito) sempre funciona.
Czarek Tomczak

12
"quando o diretório já está no sys.path". Embora totalmente verdadeiro, como poderíamos adivinhar que o diretório não estava na sys.pathpergunta? Talvez houvesse algo omitido que não vimos ou conhecemos?
S.Lott

4
Essa não é a resposta para a pergunta: se o arquivo de inicialização está ou não presente, isso não faz o python olhar para os subdiretórios. Como conseguiu centenas de votos positivos?
gented

261

Você também pode adicionar o subdiretório ao seu caminho Python para que ele seja importado como um script normal.

import sys
sys.path.insert(0, <path to dirFoo>)
import Bar

6
Parece que sua resposta não funciona com caminhos relativos, consulte stackoverflow.com/questions/279237/...
Bogdan

24
Isso faz o trabalho com caminhos relativos - você só precisa entender que um caminho relativo vai depender do que diretório que você executar a partir, o que torna esta uma solução pobre para outra coisa senão um hack rápido.
Nobar

12
Você pode fazer algo assimsys.path.append(os.path.dirname(__file__) + "/relative/path/to/module")
Falko

ousys.path.append(__name__ if __name__ != '__main__' else __loader__.fullname)
vim

1
Considere usar sys.path.insert(0, <path to dirFoo>)como ele carregará este módulo antes dos módulos de mesmo nome armazenados em outro local.
Anton Tarasenko

117
import os
import sys
lib_path = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'lib'))
sys.path.append(lib_path)

import mymodule

2
Eu gosto disso porque você tem a flexibilidade de acessar um diretório.
Charles L.

21
Você deve usar em os.path.join()vez de ingressar com '/', que será quebrado nas janelas (coxas).
0xc0de 02/08/2012

18
Isso não é confiável. Depende do diretório de trabalho atual, não do diretório que o script vive.
jamesdlin

Pode querer adicionar apenas se o caminho ainda não estiver no caminho. lib_path = os.path.abspath ('../ functions') se lib_path não estiver em sys.path: sys.path.append (lib_path)
Brent

em resposta a @jamesdlin combinando algumas respostas: what about os.path.abspath(os.path.join(__file__,'..','lib'))?
Matthew Davis

107

Basta fazer coisas simples para importar o arquivo .py de uma pasta diferente.

Digamos que você tenha um diretório como:

lib/abc.py

Em seguida, basta manter um arquivo vazio na pasta lib como nomeado

__init__.py

E então use

from lib.abc import <Your Module name>

Mantenha o __init__.pyarquivo em todas as pastas da hierarquia do módulo de importação.


79

Se você estruturar seu projeto desta maneira:

src\
  __init__.py
  main.py
  dirFoo\
    __init__.py
    Foo.py
  dirBar\
    __init__.py
    Bar.py

Então, no Foo.py, você poderá:

import dirFoo.Foo

Ou:

from dirFoo.Foo import FooObject

De acordo com o comentário de Tom, isso exige que a srcpasta esteja acessível via site_packagesou no seu caminho de pesquisa. Além disso, como ele menciona, __init__.pyé implicitamente importado quando você importa pela primeira vez um módulo nesse pacote / diretório. Normalmente __init__.pyé simplesmente um arquivo vazio.


Mencione também que o init .py é importado quando o primeiro módulo dentro desse pacote é importado. Além disso, seu exemplo só funcionará se src está nas site_packages (ou no caminho de pesquisa)
Tom Leys

3
Esta é a solução mais simples que eu estava procurando. Se o arquivo a ser importado estiver em um dos subdiretórios, esta solução é uma jóia.
Deepak GM

Eu tentei a mesma coisa e falhei. Eu não sei porque. ImportError: Nenhum módulo chamado customMath
Ming Li

9
Por que alguém iria querer importar o Foo do próprio Foo.py. Deveria ser do Bar.py, eu acho.
bhaskarc

from dirFoo import Foopara dizer Foo.bla(). Se usar import dirFoo.Fooum deve usar dirFoo.Foo.bla()- muito feio, mesmo sem estojo de camelo.
amigos estão dizendo sobre

45

O método mais fácil é usar sys.path.append ().

No entanto, você também pode estar interessado no módulo imp . Ele fornece acesso a funções internas de importação.

# mod_name is the filename without the .py/.pyc extention
py_mod = imp.load_source(mod_name,filename_path) # Loads .py file
py_mod = imp.load_compiled(mod_name,filename_path) # Loads .pyc file 

Isso pode ser usado para carregar módulos dinamicamente quando você não sabe o nome de um módulo.

Eu usei isso no passado para criar uma interface do tipo plug-in para um aplicativo, onde o usuário escreveria um script com funções específicas do aplicativo e soltaria o script em um diretório específico.

Além disso, essas funções podem ser úteis:

imp.find_module(name[, path])
imp.load_module(name, file, pathname, description)

Observe que na documentação imp.load_source e imp.load_compiled estão listados como obsoletos. imp.find_module e imp.load_module são recomendados.
Amicitas 18/10/11

@amicitas, pode fazer o favor de fornecer qualquer referência para que (eu precisar dele, e eu estou usando o Python 2.6 Estou ciente de que 2,7 docs dizer sobre estes, mas não conseguiu encontrar qualquer referência a respeito 2.6.)
0xc0de

@ 0xc0de Você pode encontrar essa declaração nos documentos do módulo imp para o python 2.7.3 e o python 2.6.7 . Parece que essas funções não estão nos documentos do python 3.2.
Amicitas

1
Para importar arquivos de origem como este no python 3.3, consulte Importar arquivo de origem python abitrário. (Python 3.3+) - Estouro de pilha . Obrigado pessoal pelas dicas aqui.
Nealmcb 4/04


23

A maneira mais fácil, sem qualquer modificação no seu script, é definir a variável de ambiente PYTHONPATH. Como o sys.path é inicializado nesses locais:

  1. O diretório que contém o script de entrada (ou o diretório atual).
  2. PYTHONPATH (uma lista de nomes de diretório, com a mesma sintaxe que a variável de shell PATH).
  3. O padrão dependente da instalação.

Apenas corra:

export PYTHONPATH=/absolute/path/to/your/module

Você sys.path contém o caminho acima, como mostrado abaixo:

print sys.path

['', '/absolute/path/to/your/module', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client', '/usr/lib/python2.7/dist-packages/ubuntuone-client', '/usr/lib/python2.7/dist-packages/ubuntuone-control-panel', '/usr/lib/python2.7/dist-packages/ubuntuone-couch', '/usr/lib/python2.7/dist-packages/ubuntuone-installer', '/usr/lib/python2.7/dist-packages/ubuntuone-storage-protocol']

13

Na minha opinião, a melhor opção é colocar __ init __.py na pasta e chamar o arquivo com

from dirBar.Bar import *

Não é recomendável usar sys.path.append () porque algo pode dar errado se você usar o mesmo nome de arquivo que o pacote python existente. Não testei isso, mas isso será ambíguo.


estranho que from dirBar.Bar import *funciona, mas não from dirBar.Bar import Bar. você sabe por que * funciona? e se eu tivesse vários arquivos no dirBar / e quisesse pegar apenas alguns deles (usando um método como o que você postou aqui)?
tester

2
@ testador: Use from dirBar import Bar.
Nobar

@ testador é porque fromindica a fonte, e tudo o importque se segue é o que pegar dessa fonte. from dirBar.Bar import Barsignifica "Da fonte, importe a própria fonte", o que não faz sentido. os *através de meios, "me dar tudo a partir da fonte"
FuriousFolder

11

A maneira rápida e suja para usuários de Linux

Se você está apenas mexendo e não se importa com problemas de implantação, pode usar um link simbólico (supondo que seu sistema de arquivos o suporte) para tornar o módulo ou pacote diretamente visível na pasta do módulo solicitante.

ln -s (path)/module_name.py

ou

ln -s (path)/package_name

Nota: Um "módulo" é qualquer arquivo com extensão .py e "pacote" é qualquer pasta que contém o arquivo __init__.py(que pode ser um arquivo vazio). Do ponto de vista de uso, os módulos e pacotes são idênticos - ambos expõem suas "definições e instruções" contidas conforme solicitado pelo importcomando.

Veja: http://docs.python.org/2/tutorial/modules.html


10
from .dirBar import Bar

ao invés de:

from dirBar import Bar

apenas no caso de haver outro dirBar instalado e confundir um leitor foo.py.


2
Eu não era capaz de fazer isso no Windows. Isso está no Linux?
Gabriel

1
Ele funcionará a partir de um script que importa o Foo. ou seja: main.py importa o dirFoo.Foo. Se você estiver tentando executar o Foo.py como um script, ele falhará. Veja stackoverflow.com/questions/72852/…
jgomo3

9

Nesse caso, para importar Bar.py para Foo.py, primeiro eu transformaria essas pastas em pacotes Python da seguinte forma:

dirFoo\
    __init__.py
    Foo.py
    dirBar\
        __init__.py
        Bar.py

Então eu faria isso no Foo.py:

from .dirBar import Bar

Se eu quisesse que o namespace se parecesse com Bar. seja qual for , ou

from . import dirBar

Se eu quisesse o espaço para nome dirBar.Bar. tanto faz . Este segundo caso é útil se você tiver mais módulos no pacote dirBar.


7

Adicione um arquivo __init__.py :

dirFoo\
    Foo.py
    dirBar\
        __init__.py
        Bar.py

Em seguida, adicione esse código ao início do Foo.py:

import sys
sys.path.append('dirBar')
import Bar

5
Se dirBarjá é um pacote de Python (pela existência de dirBar/__init__.py), não há necessidade de anexar dirBara sys.path, não? A declaração import Barde Foo.pydeve ser suficiente.
Papai

5

Exemplo de sys.path relativo:

# /lib/my_module.py
# /src/test.py


if __name__ == '__main__' and __package__ is None:
    sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
import my_module

Com base nesta resposta.


5

Bem, como você mencionou, geralmente você quer ter acesso a uma pasta com seus módulos em relação à onde seu script principal é executado, então você apenas os importa.

Solução:

Eu tenho o script D:/Books/MyBooks.pye alguns módulos (como oldies.py). Preciso importar do subdiretório D:/Books/includes:

import sys,site
site.addsitedir(sys.path[0] + '\\includes')
print (sys.path)  # Just verify it is there
import oldies

Coloque um print('done')dentro oldies.pypara verificar se tudo está indo bem. Dessa maneira, sempre funciona porque, pela definição do Python sys.path, inicializada na inicialização do programa, o primeiro item desta lista path[0], é o diretório que contém o script usado para chamar o intérprete do Python.

Se o diretório do script não estiver disponível (por exemplo, se o intérprete for chamado interativamente ou se o script for lido a partir da entrada padrão), path[0]será a string vazia, que instrui o Python a pesquisar os módulos no diretório atual primeiro. Observe que o diretório do script é inserido antes das entradas inseridas como resultado de PYTHONPATH.


1
Eu tive que usar uma barra em vez de duas barras invertidas (ou seja, site.addsitedir(sys.path[0]+'/includes')) no meu primeiro programa Python simples break_time.py: https://github.com/ltfschoen/PythonTest . Eu uso o sistema: MacOS v10.11.5, Python 2.7.12, IDLE IDE 2.7.12, Tk 8.5.9
Luke Schoen

4

Simplesmente você pode usar: from Desktop.filename import something

Exemplo:

dado que o arquivo está test.pyno diretório Users/user/Desktope importará tudo.

o código:

from Desktop.test import *

Mas certifique-se de criar um arquivo vazio chamado " __init__.py" nesse diretório


1
as importações estreladas são desaconselhadas. ver: stackoverflow.com/questions/2386714/why-is-import-bad
axolotl

eu sei que é por isso que eu em primeiro lugar escreveu import somethingentão eu disse para torná-lo mais fácil * , basicamente, não é bom para a memória RAM e também se 2 funções tem o mesmo nome que vai em massa o seu código
0x1996

3

Outra solução seria instalar o pacote py-require e, em seguida, usar o seguinte emFoo.py

import require
Bar = require('./dirBar/Bar')

Na verdade, o URL parece ser pypi.org/project/require.py e observe que ele precisa ser instalado via Pip.
Flash Sheridan

1
@FlashSheridan Não, esse é um projeto diferente. Eu apaguei o py-require porque tinha certeza de que ninguém o estava usando, embora eu não pensasse neste post. Se você ainda precisa de uma require()função, você pode dar uma olhada no meu projeto Node.py: github.com/nodepy/nodepy
Niklas R

2

Aqui está uma maneira de importar um arquivo de um nível acima, usando o caminho relativo.

Basicamente, basta mover o diretório ativo para um nível (ou qualquer local relativo), adicione-o ao seu caminho e, em seguida, mova o diretório ativo de volta para onde começou.

#to import from one level above:
cwd = os.getcwd()
os.chdir("..")
below_path =  os.getcwd()
sys.path.append(below_path)
os.chdir(cwd)

1
Eu não entendo sua lógica aqui. é a mais complicada
transilvlad

0

Eu não sou experiente em python, portanto, se houver algo errado em minhas palavras, apenas me diga. Se sua hierarquia de arquivos estiver organizada assim:

project\
    module_1.py 
    module_2.py

module_1.pydefine uma função chamada func_1(), module_2.py :

from module_1 import func_1

def func_2():
    func_1()

if __name__ == '__main__':
    func_2()

e você executa python module_2.pyno cmd, ele executa o que func_1()define. Geralmente é assim que importamos os mesmos arquivos de hierarquia. Mas quando você escreve from .module_1 import func_1em module_2.py, interpretador Python vai dizer No module named '__main__.module_1'; '__main__' is not a package. Portanto, para corrigir isso, mantemos a alteração que acabamos de fazer e movemos os dois módulos para um pacote e fazemos um terceiro módulo como chamador para executar module_2.py.

project\
    package_1\
        module_1.py
        module_2.py
    main.py

main.py :

from package_1.module_2 import func_2

def func_3():
    func_2()

if __name__ == '__main__':
    func_3()

Mas a razão por que adicionar um .antes module_1em module_2.pyé que se nós não fazemos isso e correr main.py, interpretador Python vai dizer No module named 'module_1', isso é um pouco complicado, module_1.pyé bem ao lado module_2.py. Agora eu deixo func_1()em module_1.pyfazer alguma coisa:

def func_1():
    print(__name__)

que __name__registra quem chama func_1. Agora mantemos o .antes module_1, execute main.py, ele será impresso package_1.module_1, não module_1. Indica que quem telefona func_1()está na mesma hierarquia que main.py, o .que implica que module_1está na mesma hierarquia que module_2.pyele. Portanto, se não houver um ponto, main.pyele reconhecerá module_1na mesma hierarquia que ele, ele poderá reconhecer package_1, mas não o que "está abaixo" dele.

Agora vamos complicar um pouco. Você tem um config.inie um módulo define uma função para lê-la na mesma hierarquia que 'main.py'.

project\
    package_1\
        module_1.py
        module_2.py
    config.py
    config.ini
    main.py

E, por algum motivo inevitável, é necessário chamá-lo com module_2.py, portanto, ele deve ser importado da hierarquia superior. module_2.py :

 import ..config
 pass

Dois pontos significam importação da hierarquia superior (acesso de três pontos superior a superior e assim por diante). Agora corremos main.py, o intérprete vai dizer: ValueError:attempted relative import beyond top-level package. O "pacote de nível superior" aqui é main.py. Só porque config.pyestá ao lado main.py, eles estão na mesma hierarquia, config.pynão estão "abaixo" main.pyou não são "liderados" por main.py, portanto estão além main.py. Para corrigir isso, a maneira mais simples é:

project\
    package_1\
        module_1.py
        module_2.py
    config.py
    config.ini
main.py

Eu acho que isso coincide com o princípio de organizar a hierarquia de arquivos do projeto, você deve organizar módulos com funções diferentes em pastas diferentes e deixar apenas um chamador de fora, e importar como quiser.


-5

Isso também funciona e é muito mais simples do que qualquer coisa com o sysmódulo:

with open("C:/yourpath/foobar.py") as f:
    eval(f.read())

1
Se o OP fosse codificar um caminho, eles poderiam simplesmente inseri-lo no PYTHONPATH de qualquer maneira. Eu acho que o ponto é fazê-lo de uma maneira que não codifique um caminho, pois ele quebraria em qualquer outro lugar.
Matthew Matthew

-15

Me chame de muito cauteloso, mas eu gosto de tornar o meu mais portátil porque não é seguro supor que os arquivos sempre estarão no mesmo lugar em todos os computadores. Pessoalmente, tenho o código para procurar o caminho do arquivo primeiro. Eu uso o Linux para que o meu fique assim:

import os, sys
from subprocess import Popen, PIPE
try:
    path = Popen("find / -name 'file' -type f", shell=True, stdout=PIPE).stdout.read().splitlines()[0]
    if not sys.path.__contains__(path):
        sys.path.append(path)
except IndexError:
    raise RuntimeError("You must have FILE to run this program!")

Isso é claro, a menos que você planeje agrupá-las. Mas se for esse o caso, você realmente não precisa de dois arquivos separados.


11
Super ineficiente!
0xc0de 02/08/2012

1
Os arquivos sempre estarão no mesmo local em todos os computadores, devido a um caminho relativo.
Sagar Hatekar
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.