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.

Jude Allred
fonte
2
Parece stackoverflow.com/questions/72852/… , talvez?
Joril
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.

sorin
fonte
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.

S.Lott
fonte
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
Andrew Cox
fonte
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
lefakir
fonte
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.

Deepak 'Kaseriya'
fonte
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.

bouvard
fonte
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)
monkut
fonte
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
44

Este é o PEP relevante:

http://www.python.org/dev/peps/pep-0328/

Em particular, presumindo que o dirFoo seja um diretório acima do dirBar ...

Em dirFoo \ Foo.py:

from ..dirBar import Bar
Peter Crabtree
fonte
tem funcionado para mim, acabou de adicionar o init .py em cada pasta e sucesso para importação
yussan
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']
James Gan
fonte
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.

jhana
fonte
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

nobar
fonte
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.

jgomo3
fonte
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.

Al Conrad
fonte
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
Josh
fonte
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.

Der_Meister
fonte
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.

Avenida Gez
fonte
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

0x1996
fonte
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')
Niklas R
fonte
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)
Justin Muller
fonte
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.

Findon Fassbender
fonte
-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())
jgilley
fonte
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.

SuperFamousGuy
fonte
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