Eu quero importar uma função de outro arquivo no mesmo diretório.
Às vezes funciona para mim, from .mymodule import myfunction
mas às vezes eu recebo um:
SystemError: Parent module '' not loaded, cannot perform relative import
Às vezes funciona from mymodule import myfunction
, mas às vezes também recebo:
SystemError: Parent module '' not loaded, cannot perform relative import
Não entendo a lógica aqui e não consegui encontrar nenhuma explicação. Isso parece completamente aleatório.
Alguém poderia me explicar qual é a lógica por trás de tudo isso?
python
python-3.x
python-import
John Smith Opcional
fonte
fonte
Respostas:
É bastante comum ter um layout como este ...
... com um
mymodule.py
assim ...... um
myothermodule.py
assim ...... e algo
main.py
assim ...... que funciona bem quando você executa
main.py
oumypackage/mymodule.py
, mas falha commypackage/myothermodule.py
, devido à importação relativa ...O jeito que você deve executá-lo é ...
... mas é um pouco detalhado e não combina bem com uma linha shebang como
#!/usr/bin/env python3
.A correção mais simples para esse caso, assumindo que o nome
mymodule
seja globalmente exclusivo, seria evitar o uso de importações relativas e usar apenas ...... embora, se não for exclusivo, ou a sua estrutura de pacotes for mais complexa, você precisará incluir o diretório que contém o diretório de pacotes
PYTHONPATH
e fazê-lo desta maneira ...... ou se você quiser que ele funcione "fora da caixa", você pode copiar o
PYTHONPATH
código primeiro com isso ...É meio doloroso, mas há uma pista do porquê em um e-mail escrito por um certo Guido van Rossum ...
Se a execução de scripts dentro de um pacote é um antipadrão ou não é subjetiva, mas, pessoalmente, acho que é realmente útil em um pacote que possua alguns widgets wxPython personalizados, para que eu possa executar o script para qualquer um dos arquivos de origem exibir
wx.Frame
apenas um contendo esse widget para fins de teste.fonte
os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
se você estivesse confiante de que seu módulo sempre tenha um bom funcionamento,file
você também pode usaros.path.realpath(os.path.dirname(__file__))
.sys.path.append( os.path.join( os.path.dirname(__file__), os.path.pardir ) )
...which I've always seen as an antipattern.
Não vejo como é um anti-padrão ... Parece que seria super conveniente simplesmente fazer com que as importações relativas funcionassem intuitivamente. Eu só quero poder importar coisas que eu sei que estão no mesmo diretório. Gostaria de saber qual era o seu raciocínioExplicação
Do PEP 328
Em algum momento, o PEP 338 entrou em conflito com o PEP 328 :
e para resolver o problema, o PEP 366 introduziu a variável de nível superior
__package__
:(ênfase minha)
Se
__name__
for'__main__'
,__name__.rpartition('.')[0]
retorna uma string vazia. É por isso que existe uma string vazia literal na descrição do erro:A parte relevante da
PyImport_ImportModuleLevelObject
função do CPython :O CPython gera essa exceção se não puder encontrar
package
(o nome do pacote) eminterp->modules
(acessível comosys.modules
). Comosys.modules
é "um dicionário que mapeia nomes de módulos para módulos que já foram carregados" , agora está claro que o módulo pai deve ser explicitamente importado de forma absoluta antes de executar a importação relativa .Nota: O patch da edição 18018 adicionou outro
if
bloco , que será executado antes do código acima:Se
package
(o mesmo que acima) for uma string vazia, a mensagem de erro será exibida.No entanto, você verá isso apenas no Python 3.6 ou mais recente.
Solução 1: execute seu script usando -m
Considere um diretório (que é um pacote Python ):
Todos os arquivos no pacote começam com as mesmas 2 linhas de código:
Estou incluindo essas duas linhas apenas para tornar a ordem das operações óbvia. Podemos ignorá-los completamente, pois eles não afetam a execução.
__init__.py e module.py contêm apenas essas duas linhas (ou seja, elas estão efetivamente vazias).
standalone.py tenta adicionalmente importar module.py por meio de importação relativa:
Estamos bem cientes de que
/path/to/python/interpreter package/standalone.py
irá falhar. No entanto, podemos executar o módulo com a-m
opção de linha de comando que "procurarásys.path
o módulo nomeado e executará seu conteúdo como o__main__
módulo" :-m
faz todo o material de importação para você e define automaticamente__package__
, mas você pode fazer isso sozinho noSolução 2: Defina __package__ manualmente
Trate-o como uma prova de conceito e não como uma solução real. Não é adequado para uso em código do mundo real.
O PEP 366 tem uma solução alternativa para esse problema, no entanto, é incompleto, porque a configuração
__package__
sozinha não é suficiente. Você precisará importar pelo menos N pacotes anteriores na hierarquia do módulo, em que N é o número de diretórios-pai (relativos ao diretório do script) que serão pesquisados pelo módulo que está sendo importado.Portanto,
Adicione o diretório pai do enésimo predecessor do módulo atual ao
sys.path
Remova o diretório do arquivo atual de
sys.path
Importe o módulo pai do módulo atual usando seu nome completo
Defina
__package__
com o nome completo de 2Executar a importação relativa
Vou emprestar arquivos da Solução 1 e adicionar mais alguns subpacotes:
Dessa vez, o standalone.py importará o module.py do pacote do pacote usando a seguinte importação relativa
Precisamos preceder essa linha com o código padrão, para fazê-la funcionar.
Ele nos permite executar standalone.py por nome de arquivo:
Uma solução mais geral envolvida em uma função pode ser encontrada aqui . Exemplo de uso:
Solução 3: use ferramentas e importações absolutas
Os passos são -
Substituir importações relativas explícitas por importações absolutas equivalentes
Instale
package
para torná-lo importávelPor exemplo, a estrutura de diretórios pode ser a seguinte
onde setup.py é
O restante dos arquivos foi emprestado da Solução 1 .
A instalação permitirá que você importe o pacote independentemente do seu diretório de trabalho (assumindo que não haverá problemas de nomeação).
Podemos modificar o standalone.py para usar esta vantagem (etapa 1):
Altere seu diretório de trabalho para
project
e execute/path/to/python/interpreter setup.py install --user
(--user
instala o pacote no diretório de pacotes do site ) (etapa 2):Vamos verificar se agora é possível executar standalone.py como um script:
Nota : Se você decidir seguir esse caminho, seria melhor usar ambientes virtuais para instalar pacotes isoladamente.
Solução 4: use importações absolutas e algum código padrão
Francamente, a instalação não é necessária - você pode adicionar algum código padrão ao seu script para fazer com que as importações absolutas funcionem.
Vou pegar emprestados arquivos da Solução 1 e alterar standalone.py :
Adicionar o diretório pai do pacote para
sys.path
antes de tentar importar qualquer coisa de pacote usando importações absolutos:Substitua a importação relativa pela importação absoluta:
standalone.py é executado sem problemas:
Sinto que devo avisá-lo: tente não fazer isso, principalmente se o seu projeto tiver uma estrutura complexa.
Como observação lateral, o PEP 8 recomenda o uso de importações absolutas, mas afirma que, em alguns cenários, importações relativas explícitas são aceitáveis:
fonte
__package__
manualmente se o nome está__main__
para resolver o problema?imp
módulo e defini-lo__package__
adequadamente, mas o resultado é claramente um antipadrão.AttributeError: 'PosixPath' object has no attribute 'path'
.Coloque isso dentro do arquivo __init__.py do seu pacote :
Supondo que seu pacote seja assim:
Agora use importações regulares no seu pacote, como:
Isso funciona nos python 2 e 3.
fonte
__init__.py
basicamente resolverá todos os erros de importação relativos.sys.path
porque estou preocupado que isso possa afetar outro código. (Parcialmente isso é porque eu não sei os meandros de como ele funciona.)Eu me deparei com esse problema. Uma solução alternativa de hack é importada por meio de um bloco if / else da seguinte maneira:
fonte
except:
é ruim. use emexcept ImportError:
vez disso!SystemError
aqui. (Py 3.4)if __name__ == '__main__': from mymod import as_int; else: from .mymod import as_int
.Felizmente, isso será de valor para alguém por aí - passei por meia dúzia de postagens de stackoverflow tentando descobrir importações relativas semelhantes às postadas acima aqui. Configurei tudo como sugerido, mas ainda estava batendo
ModuleNotFoundError: No module named 'my_module_name'
Desde que eu estava apenas desenvolvendo localmente e brincando, não havia criado / executado um
setup.py
arquivo. Aparentemente, eu também não tinha definido o meuPYTHONPATH
.Percebi que, quando eu executava meu código como quando os testes estavam no mesmo diretório que o módulo, não conseguia encontrar meu módulo:
No entanto, quando especifiquei explicitamente o caminho em que as coisas começaram a funcionar:
Portanto, no caso de alguém ter tentado algumas sugestões, acredita que seu código está estruturado corretamente e ainda se encontra em uma situação semelhante à minha, tente uma das seguintes opções se você não exportar o diretório atual para o seu PYTHONPATH:
$ PYTHONPATH=. python3 test/my_module/module_test.py
PYTHONPATH=.
, crie umsetup.py
arquivo com conteúdo como o seguinte e executepython setup.py development
para adicionar pacotes ao caminho:fonte
Eu precisava executar python3 no diretório principal do projeto para fazê-lo funcionar.
Por exemplo, se o projeto tiver a seguinte estrutura:
Solução
Eu rodaria o python3 dentro da pasta project_demo / e executaria uma
fonte
Para evitar esse problema, criei uma solução com o pacote de reembalagem , que funcionou para mim por algum tempo. Ele adiciona o diretório superior ao caminho da lib:
A reembalagem pode fazer importações relativas que funcionam em uma ampla variedade de casos, usando uma estratégia inteligente (inspecionando a pilha de chamadas).
fonte
se os dois pacotes estiverem no seu caminho de importação (sys.path) e o módulo / classe que você deseja estiver em example / example.py, para acessar a classe sem a importação relativa, tente:
fonte
Eu acho que a melhor solução é criar um pacote para o seu módulo: Aqui estão mais informações sobre como fazê-lo.
Depois de ter um pacote, você não precisa se preocupar com a importação relativa, basta fazer importações absolutas.
fonte
Eu tive um problema semelhante: eu precisava de um serviço Linux e de um plugin cgi que usassem constantes comuns para cooperar. A maneira 'natural' de fazer isso é colocá-los no init .py do pacote, mas não consigo iniciar o plugin cgi com o parâmetro -m.
Minha solução final foi semelhante à Solução 2 acima:
A desvantagem é que você deve prefixar as constantes (ou funções comuns) com o pkg:
fonte