Importar um arquivo de um subdiretório?

456

Eu tenho um arquivo chamado tester.py, localizado em /project.

/projectpossui um subdiretório chamado lib, com um arquivo chamado BoxTime.py:

/project/tester.py
/project/lib/BoxTime.py

Eu quero importar BoxTimede tester. Eu tentei isso:

import lib.BoxTime

O que resultou:

Traceback (most recent call last):
  File "./tester.py", line 3, in <module>
    import lib.BoxTime
ImportError: No module named lib.BoxTime

Alguma idéia de como importar BoxTimedo subdiretório?

EDITAR

O __init__.pyfoi o problema, mas não se esqueça de referir a BoxTimecomo lib.BoxTime, ou utilizar:

import lib.BoxTime as BT
...
BT.bt_function()
Adam Matan
fonte

Respostas:

536

Dê uma olhada na documentação de pacotes (Seção 6.4) aqui: http://docs.python.org/tutorial/modules.html

Em resumo, você precisa colocar um arquivo em branco chamado

__init__.py

no diretório "lib".

Greg
fonte
59
Por que parece hacky ? É assim que o python marca diretórios de importação seguros / disponíveis.
iAbstract
7
Ele não apenas marca diretórios de importação seguros / disponíveis, mas também fornece uma maneira de executar algum código de inicialização ao importar um nome de diretório.
Sadjad
32
Sim, isso é hacky e até sujo, e na minha opinião a linguagem não deve impor sua maneira de carregar arquivos no sistema de arquivos. No PHP, resolvemos o problema, permitindo que o código da terra do usuário registrasse várias funções de carregamento automático que são chamadas quando um espaço de nome / classe está ausente. Então a comunidade produziu o padrão PSR-4 e o Composer o implementa, e hoje em dia ninguém precisa se preocupar com isso. E nenhum __init__arquivo codificado estúpido (mas se você quiser, basta registrar um gancho de carregamento automático! Essa é a diferença entre hacky e hackable ).
Morgan Touverey Quilling
4
@ AurélienOomsimport sys, os; sys.path.insert(0, os.path.abspath('..')); from sibling_package.hacks import HackyHackHack
jbowman
4
python é um messy :)
Jimmy Pettersson
174
  • Crie um subdiretório chamado lib.
  • Crie um arquivo vazio chamado lib\__init__.py.
  • Em lib\BoxTime.py, escreva uma função foo()como esta:

    def foo():
        print "foo!"
    
  • No código do seu cliente no diretório acima lib, escreva:

    from lib import BoxTime
    BoxTime.foo()
    
  • Execute o seu código de cliente. Você vai ter:

    foo!

Muito mais tarde - no linux, ficaria assim:

% cd ~/tmp
% mkdir lib
% touch lib/__init__.py
% cat > lib/BoxTime.py << EOF
heredoc> def foo():
heredoc>     print "foo!"
heredoc> EOF
% tree lib
lib
├── BoxTime.py
└── __init__.py

0 directories, 2 files
% python 
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from lib import BoxTime
>>> BoxTime.foo()
foo!
hughdbrown
fonte
2
Você poderia fornecer um link para a documentação do Python, onde isso é explicado? Obrigado!
Zenon
5
Vamos tornar esse link clicável: docs.python.org/3/tutorial/modules.html#packages
Gabriel Staples
Bom passo a passo para a implementação de um pacotelib
MasterControlProgram
observação: os subdiretórios não devem conter traços ou pontos, mas os sublinhados são válidos. para mim, isso parece as mesmas restrições que para outros nomes de símbolos, mas ainda não o desenterrei no nível da documentação.
Alexander Stohr 11/03
underscores => python3 (tarde demais para editar o comentário)
Alexander Stohr
68

Você pode tentar inseri-lo em sys.path:

sys.path.insert(0, './lib')
import BoxTime
Kresimir
fonte
11
Isso é ótimo se você, por algum motivo, não puder ou não criar o arquivo .py init .
jpihl
1
Funciona se você executar o python no diretório "projeto". O "." é interpretado em relação ao seu diretório de trabalho atual, não ao diretório em que o arquivo que você está executando vive. Digamos que você cd /data, python ../project/tester.py. Então não vai funcionar.
morningstar
2
Isso funcionou para mim. Eu prefiro isso ao invés de um arquivo .py init , ele cria declarações de importação mais limpas.
Taylor Evanson
5
Isso funciona MUITO melhor e é a solução "correta". init .py bagunça pacotes como o boto que têm suas próprias pastas filho com módulos.
Dave Dopson
1
@jpihl Você precisa criar (pelo menos) um arquivo empy chamado __init__.py para permitir módulos de importação python dessa pasta. Eu tentei essa solução e funciona perfeitamente (v2.7.6).
M3nda 28/08/2015
31

Estou escrevendo isso porque todo mundo parece sugerir que você precise criar um libdiretório.

Você não precisa nomear seu subdiretório lib. Você pode nomeá-lo, anythingdesde que você coloque um __init__.py.

Você pode fazer isso digitando o seguinte comando em um shell linux:

$ touch anything/__init__.py 

Então agora você tem essa estrutura:

$ ls anything/
__init__.py
mylib.py

$ ls
main.py

Então você pode importar mylibpara o main.pyseguinte:

from anything import mylib 

mylib.myfun()

Você também pode importar funções e classes como esta:

from anything.mylib import MyClass
from anything.mylib import myfun

instance = MyClass()
result = myfun()

Qualquer função variável ou classe inserida __init__.pytambém pode ser acessada:

import anything

print(anything.myvar)

Ou assim:

from anything import myvar

print(myvar)
nurettin
fonte
Minha estrutura de pastas é utils\__init__.pye utils\myfile.py. (Os utilitários contêm os dois arquivos) É assim que estou tentando importar from utils.myfile import myMethod. Mas eu entendo ModuleNotFoundError: No module named 'utils'. O que pode estar errado? PS: Eu estou usando Djangoe tentar importar em views.pyque está no mesmo nível que utilspasta
Jagruti
É possível usar caminhos absolutos ao importar módulos e executar seu programa comPYTHONPATH=. python path/to/program.py
nurettin
21

Seu diretório lib contém um __init__.pyarquivo?

O Python usa __init__.pypara determinar se um diretório é um módulo.

Wade
fonte
16

Tente import .lib.BoxTime. Para mais informações, leia sobre a importação relativa no PEP 328 .

drrlvn
fonte
2
Acho que nunca vi essa sintaxe usada antes. Existe uma forte razão (não) para usar esse método?
tgray 11/08/09
2
Por que não foi essa a resposta? Claro, se você quiser fazer a coisa toda sobre pacotes, faça isso. Mas não era essa a pergunta original.
Travis Griggs
Isto dá-me: ValueError: Tentativa de importação relativo em não-pacote
Alex
5
Isso funciona apenas se o arquivo do qual você está importando fizer parte de um pacote. Caso contrário, você receberá o erro que @Alex apontou.
Jonathon Reinhart
8

Eu faço isso, que basicamente abrange todos os casos (verifique se você possui __init__.pyna pasta / path / to / your / lib /):

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/your/lib/folder")
import someFileNameWhichIsInTheFolder
...
somefile.foo()


Exemplo:
você tem na sua pasta de projeto:

/root/myproject/app.py

Você tem outra pasta de projeto:

/root/anotherproject/utils.py
/root/anotherproject/__init__.py

Você deseja usar /root/anotherproject/utils.pye chamar a função foo que está nele.

Então você escreve em app.py:

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../anotherproject")
import utils

utils.foo()
Mercúrio
fonte
2
se você estiver usando, os.pathprovavelmente deseja usar, em os.path.join((os.path.dirname(os.path.realpath(__file__)),'..','anotherproject')vez de codificar, o '/' na concatenação do caminho.
cowbert
Por que você não pode simplesmente ficar "../anotherproject"sem o os.path.dirname()?
Moshe Rabaev 30/07/2018
@MosheRabaev - É uma boa prática usar as funções do os.path. No caso de escrever "../anotherproject" e mover o código para o sistema operacional Windows, o código será quebrado! os.path utils sabe como retornar o caminho correto, considerando o SO no qual o código está sendo executado. para obter mais informações, docs.python.org/2/library/os.path.html
Mercury
@MosheRabaev e se você usar ".." sem o dirname(realpath(__file__)), ele calculará o caminho relativo ao seu diretório de trabalho atual quando você executar o script, não em relação ao local onde o script está.
TJ Ellis
5

Crie um arquivo vazio __init__.pyno subdiretório / lib. E adicione no início do código principal

from __future__ import absolute_import 

então

import lib.BoxTime as BT
...
BT.bt_function()

ou melhor

from lib.BoxTime import bt_function
...
bt_function()
Mik
fonte
0

Apenas uma adição a essas respostas.

Se você deseja importar todos os arquivos de todos os subdiretórios , pode adicioná-lo à raiz do seu arquivo.

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

E então você pode simplesmente importar arquivos dos subdiretórios, como se esses arquivos estivessem dentro do diretório atual.

Exemplo de trabalho

Se eu tiver o seguinte diretório com subdiretórios no meu projeto ...

.
├── a.py
├── b.py
├── c.py
├── subdirectory_a
   ├── d.py
   └── e.py
├── subdirectory_b
   └── f.py
├── subdirectory_c
   └── g.py
└── subdirectory_d
    └── h.py

Eu posso colocar o seguinte código dentro do meu a.pyarquivo

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

# And then you can import files just as if these files are inside the current directory

import b
import c
import d
import e
import f
import g
import h

Em outras palavras, esse código abstrairá de qual diretório o arquivo é proveniente.

Vencedor
fonte
-1

/project/tester.py

/project/lib/BoxTime.py

crie um arquivo em branco __init__.pyabaixo da linha até chegar ao arquivo

/project/lib/somefolder/BoxTime.py

#lib- needs possui dois itens um __init__.pye um diretório chamado somefolder #somefolderpossui dois itens boxtime.pye__init__.py

Chaitanya Gk
fonte
-3

tente isto:

from lib import BoxTime

Orane
fonte
8
sem nenhuma explicação, isso não é muito útil.
Jean-François Fabre