python - istitle - valueerror: attempted relative import beyond top-level package




Importando módulos da pasta pai (11)

Estou executando o Python 2.5.

Esta é a minha árvore de pastas:

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

(Eu também tenho __init__.py em cada pasta, omitida aqui para legibilidade)

Como eu importo o módulo de nib de dentro do módulo de life ? Eu estou esperando que é possível fazer sem mexer com o sys.path.

Nota: O módulo principal que está sendo executado está na pasta ptdraft .


Aqui está uma resposta simples, para que você possa ver como funciona, pequena e multi-plataforma.
Ele só usa módulos os ( os , sys e inspect ) por isso deve funcionar
em qualquer sistema operacional (SO), porque o Python é 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)

Para menos linhas do que isso, substitua a segunda linha com import os.path as path, sys, inspect ,
adicionar 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 da corrente
arquivo executado em 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, sempre que você quiser encontrar o arquivo de origem, use:

abspath(getsourcefile(lambda:0))

Meu código adiciona um caminho de arquivo ao sys.path , a lista de caminhos 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 o sys.path.pop(0) em uma nova linha
quando essa pasta adicionada tiver um módulo com o mesmo nome de outro módulo importado
mais tarde no programa. Você precisa remover o item de lista adicionado antes da importação, não de outros caminhos.
Se o seu programa não importar outros módulos, é seguro não excluir o caminho do arquivo porque
depois que um programa termina (ou reinicia o shell Python), todas as edições feitas em sys.path desaparecem.

Notas sobre uma variável de nome de arquivo

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

Alguns exemplos em que isso não funciona (citação this pergunta do ):

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

  • py2exe não tem um atributo __file__ , mas há uma solução alternativa
  • Quando você executa de IDLE com execute() não há __file__ atributo __file__
  • OS X 10.6 onde recebo NameError: global name '__file__' is not defined

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

import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))

As soluções acima mencionadas também estão bem. Outra solução para este problema é

Se você quiser importar alguma coisa do diretório de nível superior. Então,

from ...module_name import *

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

from ..module_name import *

Além disso, se você quiser 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 quiser.


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

import nib

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 de trabalho 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á qualquer 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.


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

Você precisa adicionar o diretório que contém ptdraft para ptdraft

Você disse que a import nib funcionou com você, o que provavelmente significa que você adicionou o próprio ptdraft (não seu pai) ao PYTHONPATH.


Se adicionar sua pasta de módulo ao PYTHONPATH não funcionou, você pode modificar a lista sys.path em seu programa onde o interpretador Python procura os módulos para importar, a documentação do python diz:

Quando um módulo chamado spam é importado, o interpretador primeiro procura por 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 da variável de shell PATH).
  • o padrão dependente da instalação.

Após a inicialização, os programas em Python podem modificar o sys.path . O diretório que contém o script 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. Isso é um erro, a menos que a substituição seja intencional.

Sabendo disso, você pode fazer o seguinte no 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

Trabalhe com bibliotecas. Crie uma biblioteca chamada nib, instale-a usando o 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. Divida em pedaços.


Você pode usar o caminho dependente do sistema operacional em "caminho de pesquisa do módulo", listado em sys.path . Então você pode facilmente adicionar o diretório pai como seguindo

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

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

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

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

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

arquivo retorna o local em que você está trabalhando


Eu postei uma resposta semelhante também à questão referente às importações de pacotes de irmãos. Você pode ver isso here . O seguinte é testado com o Python 3.6.5, (Anaconda, conda 4.5.1), máquina Windows 10.

Solução sem sys.path - hasks ou importações relativas

Configuração

Eu assumo a mesma estrutura de pastas que na pergunta

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

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

Passos

1) Adicione um setup.py à pasta raiz

O conteúdo do setup.py pode ser simplesmente

from setuptools import setup, find_packages

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

Basicamente, "qualquer" setup.py funcionaria. Este é apenas um exemplo mínimo de trabalho.

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 ajudarão você no longo prazo (quando você tem mais de um projeto em andamento ..). Os passos mais básicos são (executar na pasta raiz)

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

Para saber mais sobre isso, apenas o Google "python virtual env tutorial" ou similar. Você provavelmente nunca precisa de nenhum outro comando além de criar, ativar e desativar.

Depois de ter feito e ativado 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 instale seu projeto em estado editável

Instale seu pacote de nível superior myproject usando pip . O truque é usar o sinalizador -e 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 incluídas automaticamente no pacote instalado.

No diretório raiz, execute

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

Você também pode ver que ele é 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 mainfolder a cada importação

Neste exemplo, a ptdraft seria ptdraft . Isso tem a vantagem de você não se deparar com colisões de nomes com outros nomes de módulos (da biblioteca padrão do python ou 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!




python-import