Importando módulos da pasta pai

625

Estou executando o Python 2.5.

Esta é a minha árvore de pastas:

ptdraft/
  nib.py
  simulations/
    life/
      life.py

(Eu também tenho __init__.pyem cada pasta, omitido aqui para facilitar a leitura)

Como importo o nibmódulo de dentro do lifemódulo? Espero que seja possível fazer sem mexer com sys.path.

Nota: O módulo principal em execução está na ptdraftpasta.

Ram Rachum
fonte
1
Qual é a sua configuração PYTHONPATH?
S.Lott
2
Ross: Eu olhei lá. O que devo fazer sobre isso? Eu já tenho um __init__.py. S. Lott: Eu não sei como verificar ...
Ram Rachum
4
eco $ PYTHONPATH da casca; sistema de importação; imprima sys.path a partir do Python. docs.python.org/tutorial/…
S.Lott 03/04/09
@FlipMcF O Google é um mecanismo de pesquisa com bolhas, portanto, o fato de esse resultado ser muito alto para você não importa. Muito mais importante é o fato de o mecanismo de pesquisa sem bolhas, DuckDuckGo, também ter uma classificação muito alta.
ArtOfWarfare
3
Eu recomendo fortemente pular passado todos sys.pathou PYTHONPATHrespostas e check-out excelente resposta de NP8 . Sim, é uma leitura longa. Sim, parece muito trabalho. Mas é a única resposta que realmente resolve o problema de maneira correta e limpa.
22818 Aran-Fey

Respostas:

118

Parece que o problema não está relacionado ao fato de o módulo estar em um diretório pai ou algo assim.

Você precisa adicionar o diretório que contém ptdraftao PYTHONPATH

Você disse que import nibtrabalhou com você, o que provavelmente significa que você se adicionou ptdraft(e não o pai) ao PITONONA.

hasen
fonte
15
Ter que adicionar tudo ao pythonpath não é uma boa solução se você estiver trabalhando com muitos pacotes que nunca serão reutilizados.
Jason Cheng
@ JasonCheng: Então, por que eles são pacotes ?
Davis Herring
7
Para mim, essa resposta deu certo. No entanto, para torná-lo mais explícito, o procedimento é import syse depoissys.path.append("..\<parent_folder>")
BCJuan
603

Você pode usar importações relativas (python> = 2.5):

from ... import nib

(O que há de novo no Python 2.5) PEP 328: importações absolutas e relativas

EDIT : adicionado outro ponto '.' subir dois pacotes

f3lix
fonte
177
ValueError: Attempted relative import in non-package
Endolith 17/10/2013
18
@ endolith: Você precisa estar em um pacote, ou seja, você deve ter um arquivo .py init .
kirbyfan64sos
31
Tentativa de importação relativa além do pacote de nível superior
User
291
Para ser ainda mais preciso, você precisa de um __init__.pyarquivo.
precisa saber é o seguinte
17
Veja também a seguinte resposta, uma vez que a adição __init__.pynão é a única coisa que você tem que fazer: stackoverflow.com/questions/11536764/...
Ben fazendeiro
286

As importações relativas (como em from .. import mymodule) funcionam apenas em um pacote. Para importar 'mymodule' que está no diretório pai do seu módulo atual:

import os,sys,inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0,parentdir) 

import mymodule

editar : o __file__atributo nem sempre é fornecido. Em vez de usar os.path.abspath(__file__), agora sugeri o uso do módulo inspecionar para recuperar o nome do arquivo (e o caminho) do arquivo atual

Remi
fonte
97
algo um pouco mais curto:sys.path.insert(1, os.path.join(sys.path[0], '..'))
JHolta
8
Alguma razão para evitar sys.path.append () em vez da inserção?
Tyler
2
@ Tyler - Pode importar se em algum outro lugar parentdir, mas em um dos caminhos já especificados em sys.path, houver outro módulo com o nome 'mymodule'. Inserir o parentdircomo o primeiro elemento da sys.pathlista garante que o módulo parentdirseja importado.
Remi
5
@JHolta Se você não está lidando com pacotes, a sua é a melhor solução.
Jonathon Reinhart
5
@JHolta ótima idéia. sys.path.insert(1, os.path.realpath(os.path.pardir))funciona também
SpeedCoder5
177

Também postei uma resposta semelhante à pergunta sobre importações de pacotes de irmãos. Você pode vê-lo aqui .

Solução sem sys.pathhacks

Sumário

  • Coloque o código em uma pasta (por exemplo packaged_stuff)
  • Use o setup.pyscript create onde você usa setuptools.setup () .
  • O Pip instala o pacote no estado editável com pip install -e <myproject_folder>
  • Importar usando from packaged_stuff.modulename import function_name

Configuração

Eu assumo a mesma estrutura de pastas da pergunta

.
└── ptdraft
    ├── __init__.py
    ├── nib.py
    └── simulations
        ├── __init__.py
        └── life
            ├── __init__.py
            └── life.py

Eu chamo a .pasta raiz e, no meu caso, está localizada em C:\tmp\test_imports.

Passos

1) Adicione um setup.pyà pasta raiz

O conteúdo do setup.pypode ser simplesmente

from setuptools import setup, find_packages

setup(name='myproject', version='1.0', packages=find_packages())

Basicamente "qualquer" setup.pyfuncionaria. Este é apenas um exemplo de trabalho mínimo.

2) Use um ambiente virtual

Se você estiver familiarizado com ambientes virtuais, ative um e pule para a próxima etapa. O uso de ambientes virtuais não é absolutamente necessário, mas eles realmente o ajudarão a longo prazo (quando você tiver mais de um projeto em andamento ..). As etapas mais básicas são (executadas na pasta raiz)

  • Criar ambiente virtual
    • python -m venv venv
  • Ativar env virtual
    • . /venv/bin/activate(Linux) ou ./venv/Scripts/activate(Win)

Para saber mais sobre isso, basta pesquisar no Google "python virtualenv tutorial" ou similar. Você provavelmente nunca precisará de outros comandos além de criar, ativar e desativar.

Depois de criar e ativar um ambiente virtual, seu console deve fornecer o nome do ambiente virtual entre parênteses.

PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>

3) pip instala seu projeto em estado editável

Instale seu pacote de nível superior myprojectusando pip. O truque é usar a -ebandeira ao fazer a instalação. Dessa forma, ele é instalado em um estado editável e todas as edições feitas nos arquivos .py serão automaticamente incluídas no pacote instalado.

No diretório raiz, execute

pip install -e . (observe o ponto, significa "diretório atual")

Você também pode ver que ele está instalado usando pip freeze

(venv) PS C:\tmp\test_imports> pip install -e .
Obtaining file:///C:/tmp/test_imports
Installing collected packages: myproject
  Running setup.py develop for myproject
Successfully installed myproject
(venv) PS C:\tmp\test_imports> pip freeze
myproject==1.0

4) Importe anexando anteriormente mainfoldera cada importação

Neste exemplo, o mainfolderseria ptdraft. Isso tem a vantagem de não ocorrer colisões de nomes com outros nomes de módulos (da biblioteca padrão do python ou de módulos de terceiros).


Exemplo de uso

nib.py

def function_from_nib():
    print('I am the return value from function_from_nib!')

life.py

from ptdraft.nib import function_from_nib

if __name__ == '__main__':
    function_from_nib()

Executando life.py

(venv) PS C:\tmp\test_imports> python .\ptdraft\simulations\life\life.py
I am the return value from function_from_nib!
np8
fonte
1
Por que isso funciona? Está mudando o PYTHONPATH nos bastidores de alguma forma?
Homero Esmeraldo
2
Além disso, por que isso é melhor ou mais elegante do que usar a abordagem sys.path?
Homero Esmeraldo
5
@HomeroBarrocasSEsmeraldo Good questions. PYTHONPATH env var está intocado. Isso é instalado nos pacotes de sites , que já estão ativos sys.pathe onde o código normalmente seria instalado pelos usuários finais. Isso é melhor do que os sys.pathhacks (e você pode incluir o uso do PYTHONPATH nessa categoria de hacks de caminho) porque significa que o código em desenvolvimento deve se comportar da mesma maneira para você e para outros usuários. Por outro lado, ao usar modificações de caminho, as instruções de importação seriam resolvidas de uma maneira diferente.
wim
5
Esta é de longe, a melhor solução que eu já li. Isso cria um link para o diretório atual para que todas as atualizações do código sejam refletidas diretamente no diretório de trabalho. No caso de você precisar excluir esta entrada, exclua manualmente o arquivo egg e remova a entrada da easy-install nos dist-packages (pacotes do site, se você usou o pip).
Rakshit Kothari
2
por que tornar a dor de cabeça de importação python ainda mais complicada? Temos que escrever um arquivo de instalação, um ambiente virtual, uma instalação de pip? AMD!
Emerson Xu
68

Você pode usar o caminho dependente do SO no "caminho de pesquisa do módulo", listado em sys.path . Assim, você pode adicionar facilmente o diretório pai como segue

import sys
sys.path.insert(0,'..')

Se você deseja adicionar o diretório pai-pai,

sys.path.insert(0,'../..')

Isso funciona no python 2 e 3.

Shinbero
fonte
6
Isso é relativo ao diretório atual , nem mesmo necessariamente ao que contém o script principal.
Davis Herring
57

Se a adição da pasta do módulo ao PYTHONPATH não funcionou, você pode modificar a lista sys.path no seu programa em que o interpretador Python procura os módulos a serem importados, a documentação do python diz:

Quando um módulo chamado spam é importado, o intérprete primeiro procura um módulo interno com esse nome. Se não for encontrado, ele procurará um arquivo chamado spam.py em uma lista de diretórios fornecida pela variável sys.path. sys.path é inicializado a partir desses locais:

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

Após a inicialização, os programas Python podem modificar o sys.path . O diretório que contém o script que está sendo executado é colocado no início do caminho de pesquisa, à frente do caminho da biblioteca padrão. Isso significa que os scripts nesse diretório serão carregados em vez de módulos com o mesmo nome no diretório da biblioteca. Este é um erro, a menos que a substituição seja intencional.

Sabendo disso, você pode fazer o seguinte em seu programa:

import sys
# Add the ptdraft folder path to the sys.path list
sys.path.append('/path/to/ptdraft/')

# Now you can import your module
from ptdraft import nib
# Or just
import ptdraft
Ricardo Murillo
fonte
6
Sua resposta é boa, mas nem sempre funciona e não é muito portátil. Se o programa foi movido para um novo local, /path/to/ptdraftteria que ser editado. Existem soluções que elaboram o diretório atual do arquivo e o importam da pasta pai dessa maneira também.
Edward
@ Edward, quais são essas técnicas?
Shuklaswag 16/09
1
@Shuklaswag, por exemplo, a resposta stackoverflow.com/questions/714063/… .
Edward
50

Não sabe muito sobre o python 2.
No python 3, a pasta pai pode ser adicionada da seguinte maneira:

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

... e então é possível importar módulos dele

Rob Ellenbroek
fonte
13
Isso funciona apenas se o diretório de trabalho atual for tal que o '..'leve ao diretório com o módulo em questão.
user5359531
31

Aqui está uma resposta simples, para que você possa ver como funciona, pequeno e multiplataforma.
Ele usa apenas módulos internos ( os, syse inspect), portanto, deve funcionar
em qualquer sistema operacional (SO) porque o Python foi projetado para isso.

Código mais curto para resposta - menos linhas e variáveis

from inspect import getsourcefile
import os.path as path, sys
current_dir = path.dirname(path.abspath(getsourcefile(lambda:0)))
sys.path.insert(0, current_dir[:current_dir.rfind(path.sep)])
import my_module  # Replace "my_module" here with the module name.
sys.path.pop(0)

Por menos linhas que isso, substitua a segunda linha por import os.path as path, sys, inspect,
adicione inspect.no início de getsourcefile(linha 3) e remova a primeira linha.
- no entanto, isso importa todo o módulo, portanto, pode precisar de mais tempo, memória e recursos.

O código da minha resposta ( versão mais longa )

from inspect import getsourcefile
import os.path
import sys

current_path = os.path.abspath(getsourcefile(lambda:0))
current_dir = os.path.dirname(current_path)
parent_dir = current_dir[:current_dir.rfind(os.path.sep)]

sys.path.insert(0, parent_dir)

import my_module  # Replace "my_module" here with the module name.

Ele usa um exemplo de uma resposta de estouro de pilha. Como obtenho o caminho do
arquivo executado atualmente no Python?
para encontrar a fonte (nome do arquivo) do código em execução com uma ferramenta interna.

from inspect import getsourcefile  
from os.path import abspath  

Em seguida, onde você quiser encontrar o arquivo de origem, basta usar:

abspath(getsourcefile(lambda:0))

Meu código adiciona um caminho de arquivo à lista de caminhossys.path do python,
porque isso permite que o Python importe módulos dessa pasta.

Depois de importar um módulo no código, é uma boa ideia executar sys.path.pop(0)uma nova linha
quando a pasta adicionada tiver um módulo com o mesmo nome que outro módulo importado
posteriormente no programa. Você precisa remover o item da lista adicionado antes da importação, e não outros caminhos.
Se o seu programa não importar outros módulos, é seguro não excluir o caminho do arquivo, porque
após o término de um programa (ou a reinicialização do shell do Python), as edições são feitas para sys.pathdesaparecer.

Notas sobre uma variável de nome de arquivo

Minha resposta não usa a __file__variável para obter o caminho do arquivo / nome do arquivo em execução
, porque os usuários aqui a descrevem como não confiável . Você não deve usá-lo
para importar módulos da pasta pai em programas usados ​​por outras pessoas.

Alguns exemplos em que não funciona (cite esta questão do Stack Overflow):

• não pode ser encontrado em algumas plataformas • às vezes não é o caminho completo do arquivo

  • py2exenão tem um __file__atributo, mas há uma solução alternativa
  • Quando você executa a partir do IDLE com execute()nenhum __file__atributo
  • OS X 10.6 onde eu recebo NameError: global name '__file__' is not defined
Edward
fonte
1
Fiz isso e removi a nova entrada de caminho sys.path.pop(0)imediatamente após importar o módulo desejado. No entanto, as importações subsequentes ainda foram para esse local. Por exemplo, eu tenho app/config, app/toolse app/submodule/config. De submodule, insiro app/para importar tools, removo app/e tento importar config, mas recebo em app/configvez de app/submodule/config.
user5359531
Eu descobri que uma das toolsimportações também estava importando configdo diretório pai. Então, quando mais tarde tentei sys.path.pop(0); import configentrar submodule, esperando conseguir app/submodule/config, estava realmente conseguindo app/config. Evidentemente, o Python retorna uma versão em cache de um módulo com o mesmo nome, em vez de realmente verificar o sys.pathmódulo correspondente a esse nome. sys.pathneste caso, estava sendo alterado corretamente, o Python simplesmente não estava verificando devido ao carregamento do módulo com o mesmo nome.
user5359531
Eu acho que esta é a referência exata para o problema que tive: docs.python.org/3/reference/import.html#the-module-cache
#
Algumas informações úteis do 5.3.1. O cache do módulo na página de documentação : Durante a importação, o nome do módulo é pesquisado em sys.modules ... sys.modules é gravável. A exclusão de uma chave invalidará a entrada de cache do módulo nomeado, fazendo com que o Python pesquise novamente na próxima importação. ... Cuidado, porém, como se você mantiver uma referência ao objeto do módulo, invalide seu cache e depois importe novamente, os dois objetos serão diferentes. Por outro lado, importlib.reload()vai reutilizar e reinicializar o conteúdo do módulo ...
Edward
Ainda mais curto: os.path.realpath(getsourcefile(lambda:0) + "/../.."). O arquivo de origem atual será anexado com dois símbolos de diretório pai. Eu não usei os.path.sepcomo getsourcefileera retorna uma string usando /mesmo no Windows. realpathcuidará de remover duas entradas de diretório do caminho completo (neste caso, o nome do arquivo e, em seguida, o diretório atual) que fornece o diretório pai.
Coburn
28

Aqui está uma solução mais genérica que inclui o diretório pai no sys.path (funciona para mim):

import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
Mike
fonte
import os, sys\n sys.path.insert(0,os.path.pardir) mesma coisa, mas sorther :) (sem avanços de linha nos comentários)
Anti Veeranna
@antiveeranna, se você usar os.path.pardir, não estará obtendo o caminho sys.path.insert()
real
1
Esta resposta usa a __file__variável que pode não ser confiável (nem sempre é o caminho completo do arquivo, não funciona em todos os sistemas operacionais etc.), como os usuários do StackOverflow mencionaram frequentemente. Alterar a resposta para não incluí-la causará menos problemas e será mais compatível. Para mais informações, consulte stackoverflow.com/a/33532002/3787376 .
Edward
23

Eu achei a seguinte maneira para importar um pacote do diretório pai do script. No exemplo, eu gostaria de importar funções env.pydo app.dbpacote.

.
└── my_application
    └── alembic
        └── env.py
    └── app
        ├── __init__.py
        └── db
import os
import sys
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)
Yu N.
fonte
Então, um bom one-liner:sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
juzzlin 12/09/19
Modificar sys.pathé geralmente uma má ideia. Você precisa de alguma maneira ( por exemplo , PYTHONPATH) para que sua biblioteca seja acessível a outro código (ou então não é realmente uma biblioteca); basta usar isso o tempo todo!
Davis Herring
14

As soluções acima mencionadas também são boas. Outra solução para esse problema é

Se você deseja importar algo do diretório de nível superior. Então,

from ...module_name import *

Além disso, se você deseja importar qualquer módulo do diretório pai. Então,

from ..module_name import *

Além disso, se você deseja importar qualquer módulo do diretório pai. Então,

from ...module_name.another_module import *

Dessa forma, você pode importar qualquer método específico, se desejar.

Ravin Gupta
fonte
6
Parece que deveria ser, mas por algum motivo isso não funciona da mesma maneira que os sys.path.appendhacks acima e não consigo importar minha biblioteca dessa maneira. Isto é o que eu tentei primeiro a fazer antes de começar a google :) Ok, foi estaValueError: attempted relative import beyond top-level package
juzzlin
10

Para mim, o mais curto e meu oneliner favorito para acessar o diretório pai é:

sys.path.append(os.path.dirname(os.getcwd()))

ou:

sys.path.insert(1, os.path.dirname(os.getcwd()))

os.getcwd () retorna o nome do diretório ativo atual, os.path.dirname (directory_name) retorna o nome do diretório para o passado.

Na verdade, na minha opinião, a arquitetura do projeto Python deve ser feita da maneira que nenhum módulo do diretório filho usará nenhum módulo do diretório pai. Se algo assim acontecer, vale a pena repensar sobre a árvore do projeto.

Outra maneira é adicionar o diretório pai à variável de ambiente do sistema PYTHONPATH.

zviad
fonte
2
+1 por mencionar a possibilidade de ter que reestruturar o projeto (em vez de lançar um hack no problema)
semore_1267
isso não obter o pai, ele começa o atual
Ricky Levi
9

Em um caderno Jupyter

Desde que você esteja trabalhando em um notebook Jupyter, esta solução curta pode ser útil:

%cd ..
import nib

Funciona mesmo sem um __init__.py arquivo.

Eu testei com o Anaconda3 no Linux e Windows 7.

Thomas R
fonte
2
Não faria sentido adicionar %cd -após a importação, para que o diretório atual do notebook permaneça inalterado.
Dror 17/04
8

import sys sys.path.append('../')

Andong Zhan
fonte
8
isso não funcionará quando executado a partir de um diretório diferente.
Shatlyk Ashyralyyev
5

Quando não está em um ambiente de pacote com __init__.pyarquivos, a biblioteca pathlib (incluída em> = Python 3.4) torna muito conciso e intuitivo anexar o caminho do diretório pai ao PYTHONPATH:

import sys
from pathlib import Path
sys.path.append(str(Path('.').absolute().parent))
importa
fonte
é possível usar o init .py para contornar esse problema?
ArtificiallyIntelligence
4

mesmo tipo de estilo que a resposta anterior - mas em menos linhas: P

import os,sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0,parentdir)

O arquivo retorna o local em que você está trabalhando

YFP
fonte
Para mim, arquivo é o nome do arquivo sem o caminho incluído. Eu corro usando "ipy filename.py".
precisa
Olá @CurtisYallop no exemplo acima, estamos adicionando o diretório que contém o arquivo [no qual estamos atualmente] em que o arquivo python está. A chamada os.path.dirname com o nome do arquivo deve retornar o caminho do arquivo e nós está adicionando ISSO ao caminho e não ao arquivo explicitamente - HTH's :-)
YFP
Você está assumindo que __file__ sempre contém o caminho mais o arquivo. Às vezes, ele contém apenas o nome do arquivo sem o caminho.
Curtis Yallop 12/03
@ CurtisYallop - de maneira alguma, senhor, estou assumindo que arquivo é o nome do arquivo e estou usando os.path.dirname () para obter o caminho para o arquivo. Você tem um exemplo disso não funcionando? Eu adoraria os passos para reproduzir
YFP
1
@CurtisYallop - Não, eu não sou! Estou dizendo que: print / _ / file / _ / fornecerá o nome do arquivo no seu exemplo acima "file.py" - então estou dizendo que você pode usar os.path.dirname () para obter o caminho completo . Se você está ligando de um local estranho e gostaria desse relacional, pode encontrar facilmente seu diretório de trabalho através do módulo os - Eita!
YFP
1

Trabalhar com bibliotecas. Crie uma biblioteca chamada nib, instale-a usando setup.py, deixe-a residir nos pacotes do site e seus problemas serão resolvidos. Você não precisa encher tudo o que faz em um único pacote. Quebrá-lo em pedaços.

user3181121
fonte
1
Sua ideia é boa, mas algumas pessoas podem querer agrupar seu módulo com seu código - talvez para torná-lo portátil e não exigir a inserção de seus módulos site-packagessempre que executá-lo em um computador diferente.
Edward
0

Em um sistema Linux, você pode criar um link virtual da pasta "life" para o arquivo nib.py. Em seguida, você pode simplesmente importá-lo como:

import nib
Erel Segal-Halevi
fonte