Importando arquivos de uma pasta diferente

1407

Eu tenho a seguinte estrutura de pastas.

application/app/folder/file.py

e quero importar algumas funções do file.py em outro arquivo Python que reside em

application/app2/some_folder/some_file.py

eu tentei

from application.app.folder.file import func_name

e algumas outras tentativas, mas até agora não consegui importar corretamente. Como posso fazer isso?

Ivan
fonte
Ler a documentação oficial me ajudou muito! docs.python.org/3/reference/…
Kavin Raju S

Respostas:

1445

Por padrão, você não pode. Ao importar um arquivo, o Python pesquisa apenas o diretório atual, o diretório do qual o script do ponto de entrada está sendo executado, e sys.pathque inclui locais como o diretório de instalação do pacote (na verdade, é um pouco mais complexo que isso, mas abrange a maioria dos casos) .

No entanto, você pode adicionar ao caminho do Python em tempo de execução:

# some_file.py
import sys
# insert at 1, 0 is the script path (or '' in REPL)
sys.path.insert(1, '/path/to/application/app/folder')

import file
Cameron
fonte
356
sys.path.append('/path/to/application/app/folder')é mais limpo imo
pseudosudo
387
@pseudosudo: Sim, é, mas inseri-lo no início tem o benefício de garantir que o caminho seja pesquisado antes de outros (mesmo os integrados) no caso de conflitos de nomes.
Cameron
8
@kreativitea - sys.pathretorna a list, não a deque, e seria bobagem converter a listpara ae para dequetrás.
ArtOfWarfare
37
É considerado como uma maneira pitônica de gerenciar arquivos .py em pastas? Eu estou pensando ... por que não é suportado por padrão? não faz sentido manter todos os arquivos .py em um único diretório.
Ofir
49
@Ofir: Não, esta não é uma boa solução python limpa. Em geral, você deve usar pacotes (que são baseados em árvores de diretório). Essa resposta foi específica da pergunta e, por algum motivo, continua acumulando um grande número de votos.
Cameron
709

Nada de errado com:

from application.app.folder.file import func_name

Apenas certifique-se de que foldertambém contém um __init__.py, isso permite que ele seja incluído como um pacote. Não sei por que as outras respostas falam sobre PYTHONPATH.

Joey
fonte
47
Porque isso não cobre os casos em que a modificação PYTHONPATHé necessária. Digamos que você tenha duas pastas no mesmo nível: Ae B. Atem um __init.py__. Tente importar algo de Bdentro A.
msvalkon
32
O que há dentro do arquivo init.pyou __init__.py?
Lorem Ipsum Dolor
48
@Xinyang Pode ser um arquivo vazio. Sua própria existência diz ao Python para tratar o diretório como um pacote.
jay
9
É claro que esta resposta assume que applicatione appjá são pacotes (ou seja, você já tem __init__.pyem ambos). Como o resultado de adicionar __init__.pytambém folder, application.app.foldertorna-se uma (sub) pacote , a partir do qual você pode acessar o módulo application.app.folder.file , cujo símbolo func_nameagora pode ser importado
Yibo Yang
30
O que quer que eu tente, isso não vai funcionar. Eu quero importar de um diretório "irmão", então um para cima e para baixo. Todos têm __ init __. Py's, incluindo pai. Este python é 3 específico?
DasWesen
111

Quando os módulos estão em locais paralelos, como na pergunta:

application/app2/some_folder/some_file.py
application/app2/another_folder/another_file.py

Essa abreviação torna um módulo visível para o outro:

import sys
sys.path.append('../')
slizb
fonte
24
Como uma advertência: funciona desde que o script de importação seja executado a partir do diretório que o contém. Caso contrário, o diretório pai de qualquer outro diretório em que o script é executado será anexado ao caminho e a importação falhará.
Carl Smith
14
Para evitar isso, podemos obter o diretório pai do arquivosys.path.append(os.path.dirname(os.path.abspath(__file__)))
Rahul
Isso não funcionou para mim - eu tinha para adicionar um dirname adicional lá para subir de volta para o pai, de modo que a execução cli/foo.pyda linha de comando foi capaz deimport cli.bar
RCross
2
@Rahul, sua solução não funciona para os reservatórios interativos
towi_parallelism
Se você executá-lo a partir da pasta raiz (por exemplo, pasta do aplicativo), provavelmente está bem em sys.path.append('.')importar o módulo usando from app2.some_folder.some_file import your_function. Alternativamente, o que funciona para mim está sendo executado a python3 -m app2.another_folder.another_filepartir da pasta raiz.
viciado
68

Primeiro importe o sys em name-file.py

 import sys

Segundo acrescente o caminho da pasta em name-file.py

sys.path.insert(0, '/the/folder/path/name-package/')

Terceiro Crie um arquivo em branco chamado __ init __.py no seu subdiretório (isso informa ao Python que é um pacote)

  • name-file.py
  • nome-pacote
    • __ init __.py
    • name-module.py

Quarta importação do módulo dentro da pasta em name-file.py

from name-package import name-module
Alex Montoya
fonte
5
Com a pasta de nomes logo abaixo de name-file.py, isso deve funcionar mesmo sem o sys.path.insertcomando- Como tal, a resposta deixa a questão, se esta solução funcionar mesmo quando a pasta de nomes estiver localizada em um local arbitrário.
Bastian
55

Eu acho que uma maneira ad-hoc seria usar a variável de ambientePYTHONPATH conforme descrito na documentação: Python2 , Python3

# Linux & OSX
export PYTHONPATH=$HOME/dirWithScripts/:$PYTHONPATH

# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%
Ax3l
fonte
Aguarde, eu substituirei myScripts pelo nome do arquivo?
Vladimir Putin
4
não, com o caminho do diretório para o seu arquivo .py
Ax3l
1
Infelizmente, se você estiver usando o Anaconda, isso não funcionará, pois o PYTHONPATH não é realmente usado internamente!
information_interchange
Para alterações (recentes) no anaconda, consulte este SO para fluxos de trabalho e comentários para soluções alternativas: stackoverflow.com/questions/17386880/… De maneira geral, crie e instale pequenos pacotes em vez de invadir os diretórios de importação.
Ax3l 14/04
47

As respostas aqui estão faltando em clareza, isso é testado no Python 3.6

Com esta estrutura de pastas:

main.py
|
---- myfolder/myfile.py

Onde myfile.pyestá o conteúdo:

def myfunc():
    print('hello')

A declaração de importação em main.pyé:

from myfolder.myfile import myfunc
myfunc()

e isso imprimirá olá .

danday74
fonte
9
adicionando uma inicialização .py (Vazio) arquivo de configuração em myfolder trabalhou para mim em linux (y)
Vincent
7
@Vincent você quis dizer __init__.py?
mrgloom
2
@mrgloom de fato
Vincent
3
Por algum motivo, adicionar __init__.pynão funciona para mim. Estou usando Py 3.6.5 no Ubuntu 18. Ele funciona em PyCharm, mas não a partir do terminal
Crearo Rotar
9
Isso não tem nenhuma relação com a pergunta sobre a importação de arquivos de uma ramificação diferente da árvore de arquivos que o diretório de trabalho atual.
Alexander Rossa
45

Seu problema é que o Python está procurando no diretório Python esse arquivo e não o encontra. Você deve especificar que está falando sobre o diretório em que está e não sobre o diretório Python.

Para fazer isso, você altera isso:

from application.app.folder.file import func_name

para isso:

from .application.app.folder.file import func_name

Ao adicionar o ponto que você está dizendo, procure nesta pasta a pasta do aplicativo em vez de procurar no diretório Python.

CianB
fonte
2
isto é o que fixa me
Phi
32

Pelo que sei, adicione um __init__.pyarquivo diretamente na pasta das funções que você deseja importar fará o trabalho.

Vaibhav Singh
fonte
7
apenas se o script que quer incluir esse outro diretório já está no sys.path
Ax3l
2
Eu usei sys.path.append(tools_dir)no Windows e não preciso adicionar um __init__.py' file in my directory tools_dir`
herve-guerin
25

No Python 3.4 e posterior, você pode importar diretamente de um arquivo de origem (link para a documentação) . Esta não é a solução mais simples, mas estou incluindo esta resposta para que esteja completa.

Aqui está um exemplo. Primeiro, o arquivo a ser importado, denominado foo.py:

def announce():
    print("Imported!")

O código que importa o arquivo acima, inspirado fortemente no exemplo da documentação:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

foo = module_from_file("foo", "/path/to/foo.py")

if __name__ == "__main__":
    print(foo)
    print(dir(foo))
    foo.announce()

A saída:

<module 'foo' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

Observe que o nome da variável, o nome do módulo e o nome do arquivo não precisam corresponder. Este código ainda funciona:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

baz = module_from_file("bar", "/path/to/foo.py")

if __name__ == "__main__":
    print(baz)
    print(dir(baz))
    baz.announce()

A saída:

<module 'bar' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

A importação de módulos de programação foi introduzida no Python 3.1 e oferece mais controle sobre como os módulos são importados. Consulte a documentação para obter mais informações.

wecsam
fonte
11
Não sei se alguém tentou entender isso, mas acho que é muito complicado.
Dan
23

Trabalhou para mim em python3 no linux

import sys  
sys.path.append(pathToFolderContainingScripts)  
from scriptName import functionName #scriptName without .py extension  
dsg38
fonte
6
sys.path.append("/home/linux/folder/")- Certifique-se não usar um atalho por exemplo"~/folder/"
Dago
22

Experimente as importações relativas do Python:

from ...app.folder.file import func_name

Cada ponto inicial é outro nível mais alto na hierarquia, começando com o diretório atual.


Problemas? Se isso não está funcionando para você, provavelmente você está ficando pouco com as importações relativas de muitas pegadinhas. Leia as respostas e os comentários para obter mais detalhes: Como corrigir "Tentativa de importação relativa em não pacote", mesmo com __init__.py

Dica: tenha __init__.pyem todos os níveis de diretório. Você pode precisar python -m application.app2.some_folder.some_file(deixando de fora .py) executar o diretório de nível superior ou ter esse diretório de nível superior em seu PYTHONPATH. Ufa!

Zectbumo
fonte
22

Fui confrontado com o mesmo desafio, especialmente ao importar vários arquivos, foi assim que consegui superá-lo.

import os, sys

from os.path import dirname, join, abspath
sys.path.insert(0, abspath(join(dirname(__file__), '..')))

from root_folder import file_name
Erick Mwazonga
fonte
2
Sua resposta seria mais útil se você pudesse explicar o que faz de maneira diferente de uma importação comum?
precisa saber é o seguinte
1
Eu tinha /path/dir1/__init__.py e /path/dir1/mod.py. Para /path/some.py de dir1.mod, a função de importação funcionou. Quando em /path/dir2/some.py, ele só funcionou depois que eu copiei e colei a resposta acima na parte superior do arquivo. Não queria editar meu caminho, pois nem todos os projetos python que tenho estão em / path /.
jwal
19

Considerando-se applicationcomo o diretório raiz para o seu projeto python, criar um vazio __init__.pyarquivo em application, appe folderpastas. Em seguida, some_file.pyfaça as alterações da seguinte maneira para obter a definição de func_name:

import sys
sys.path.insert(0, r'/from/root/directory/application')

from application.app.folder.file import func_name ## You can also use '*' wildcard to import all the functions in file.py file.
func_name()
ChandanK
fonte
deve ser: sys.path.insert (0, r '/ from / root / directory')
Bolaka 13/02/16
18

Usar sys.path.append com um caminho absoluto não é ideal ao mover o aplicativo para outros ambientes. Usar um caminho relativo nem sempre funcionará porque o diretório de trabalho atual depende de como o script foi chamado.

Como a estrutura da pasta do aplicativo é fixa, podemos usar o os.path para obter o caminho completo do módulo que queremos importar. Por exemplo, se esta é a estrutura:

/home/me/application/app2/some_folder/vanilla.py
/home/me/application/app2/another_folder/mango.py

E digamos que você queira importar o módulo "manga". Você pode fazer o seguinte em vanilla.py:

import sys, os.path
mango_dir = (os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ '/another_folder/')
sys.path.append(mango_dir)
import mango

Obviamente, você não precisa da variável mango_dir.

Para entender como isso funciona, veja este exemplo de sessão interativa:

>>> import os
>>> mydir = '/home/me/application/app2/some_folder'
>>> newdir = os.path.abspath(os.path.join(mydir, '..'))
>>> newdir
    '/home/me/application/app2'
>>> newdir = os.path.abspath(os.path.join(mydir, '..')) + '/another_folder'
>>> 
>>> newdir
'/home/me/application/app2/another_folder'
>>> 

E verifique a documentação do os.path .

Nagev
fonte
13

Isso funciona para mim no Windows

# some_file.py on mainApp/app2 
import sys
sys.path.insert(0, sys.path[0]+'\\app2')

import some_file
Emeeus
fonte
10

Sou bastante especial: uso Python no Windows!

Acabei de completar as informações: para Windows e Linux, o caminho relativo e o absoluto funcionam sys.path(eu preciso de caminhos relativos porque uso meus scripts em vários PCs e em diferentes diretórios principais).

E ao usar o Windows ambos \e /pode ser usado como separador para nomes de arquivos e, é claro, você deve dobrar \para cadeias de caracteres Python,
alguns exemplos válidos:

sys.path.append('c:\\tools\\mydir')
sys.path.append('..\\mytools')
sys.path.append('c:/tools/mydir')
sys.path.append('../mytools')

(observação: acho que /é mais conveniente que \, caso seja menos 'nativo do Windows' porque é compatível com Linux e mais simples de gravar e copiar no Windows Explorer)

herve-guerin
fonte
4
os.path.join ('tools', 'mydir')
Corey Goldberg
7

Se o objetivo de carregar um módulo a partir de um caminho específico for ajudá-lo durante o desenvolvimento de um módulo customizado, você poderá criar um link simbólico na mesma pasta do script de teste que aponta para a raiz do módulo customizado. Essa referência de módulo terá precedência sobre quaisquer outros módulos instalados com o mesmo nome para qualquer script executado nessa pasta.

Eu testei isso no Linux, mas deve funcionar em qualquer sistema operacional moderno que suporte links simbólicos.

Uma vantagem dessa abordagem é que você pode apontar para um módulo que está em sua própria cópia de trabalho da filial SVC local, o que pode simplificar bastante o tempo do ciclo de desenvolvimento e reduzir os modos de falha do gerenciamento de diferentes versões do módulo.

Timothy C. Quinn
fonte
7

No meu caso, tive uma aula para importar. Meu arquivo ficou assim:

# /opt/path/to/code/log_helper.py
class LogHelper:
    # stuff here

No meu arquivo principal, incluí o código via:

import sys
sys.path.append("/opt/path/to/code/")
from log_helper import LogHelper
schmudu
fonte
1
@ not2qubit sys não foi importado na resposta.
Walter Walter
7

Eu me deparei com a mesma pergunta várias vezes, então gostaria de compartilhar minha solução.

Versão Python: 3.X

A solução a seguir é para alguém que desenvolve seu aplicativo no Python versão 3.X porque o Python 2 não é suportado desde 1 / jan / 2020 .

Estrutura do Projeto

No python 3, você não precisa __init__.pyno subdiretório do projeto devido aos pacotes de espaço para nome implícito . Consulte Is init .py não é necessário para pacotes no Python 3.3 ou superior

Project 
├── main.py
├── .gitignore
|
├── a
|   └── file_a.py
|
└── b
    └── file_b.py

Declaração do Problema

Em file_b.py, eu gostaria de importar uma classe Ana file_a.pypasta a.

Soluções

# 1 Uma maneira rápida, mas suja

Sem instalar o pacote, como você está desenvolvendo um novo projeto

Usando o try catchpara verificar se os erros. Exemplo de código:

import sys
try:
    # The insertion index should be 1 because index 0 is this file
    sys.path.insert(1, '/absolute/path/to/folder/a')  # the type of path is string
    # because the system path already have the absolute path to folder a
    # so it can recognize file_a.py while searching 
    from file_a import A
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

# 2 Instale seu pacote

Depois de instalar seu aplicativo (nesta postagem, o tutorial de instalação não está incluído)

Você pode simplesmente

try:
    from __future__ import absolute_import
    # now it can reach class A of file_a.py in folder a 
    # by relative import
    from ..a.file_a import A  
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

Feliz codificação!

WY Hsu
fonte
para obter mais informações sobre importações absolutas
WY Hsu
3

Eu estava trabalhando no projeto aque queria que os usuários instalassem via pip install acom a seguinte lista de arquivos:

.
├── setup.py
├── MANIFEST.in
└── a
    ├── __init__.py
    ├── a.py
    └── b
        ├── __init__.py
        └── b.py

setup.py

from setuptools import setup

setup (
  name='a',
  version='0.0.1',
  packages=['a'],
  package_data={
    'a': ['b/*'],
  },
)

MANIFEST.in

recursive-include b *.*

a / init .py

from __future__ import absolute_import

from a.a import cats
import a.b

a / a.py

cats = 0

a / b / init .py

from __future__ import absolute_import

from a.b.b import dogs

a / b / b.py

dogs = 1

Instalei o módulo executando o seguinte no diretório com MANIFEST.in:

python setup.py install

Então, de um local totalmente diferente no meu sistema de arquivos, /moustache/armwrestleeu fui capaz de executar:

import a
dir(a)

O que confirmou que de a.catsfato era igual a 0 e, na a.b.dogsverdade, igual a 1, como pretendido.

duhaime
fonte
2

Em vez de apenas fazer um import ..., faça o seguinte:

from <MySubFolder> import <MyFile>

MyFile está dentro do MySubFolder.

Galileoomega
fonte
1

Você pode atualizar o shell do Python pressionando f5 ou vá em Executar-> Executar Módulo. Dessa forma, você não precisa alterar o diretório para ler algo do arquivo. Python mudará automaticamente o diretório. Mas se você quiser trabalhar com arquivos diferentes de um diretório diferente no Python Shell, poderá alterar o diretório em sys, como Cameron disse anteriormente.

IOstream
fonte
1

Então, eu tinha apenas clicado com o botão direito do mouse no meu IDE e adicionado um novo foldere estava me perguntando por que não consegui importar dele. Mais tarde, percebi que precisava clicar com o botão direito do mouse e criar um pacote Python, e não uma pasta clássica do sistema de arquivos. Ou um método post-mortem adicionando um __init__.py(que faz o python tratar a pasta do sistema de arquivos como um pacote) como mencionado em outras respostas. Adicionando esta resposta aqui para o caso de alguém seguir esse caminho.

mithunpaul
fonte
1

Você pode usar importlib para importar módulos nos quais deseja importar um módulo de uma pasta usando uma sequência como esta:

import importlib

scriptName = 'Snake'

script = importlib.import_module('Scripts\\.%s' % scriptName)

Este exemplo possui um main.py, que é o código acima, uma pasta chamada Scripts e, em seguida, você pode chamar o que precisar dessa pasta, alterando a scriptNamevariável Você pode usar scriptpara fazer referência a este módulo. como se eu tivesse uma função chamada Hello()no módulo Snake, você pode executar esta função fazendo o seguinte:

script.Hello()

Eu testei isso no Python 3.6

Dextron
fonte
0

Eu já tive esses problemas várias vezes. Eu vim muito para essa mesma página. No meu último problema, tive que executar o serverde um diretório fixo, mas sempre que depurava, queria executar a partir de subdiretórios diferentes.

import sys
sys.insert(1, /path) 

fez não trabalho para mim porque em módulos diferentes Eu tive que ler diferentes * .csv arquivos que foram todas no mesmo diretório.

No final, acho que o que funcionou para mim não foi pitônico, mas:

Eu usei um if __main__ no topo do módulo que eu queria depurar , que é executado a partir de um caminho diferente do habitual.

Assim:

# On top of the module, instead of on the bottom
import os
if __name__ == '__main__':
    os.chdir('/path/for/the/regularly/run/directory')
B Furtado
fonte