também apenas como referência, parece que importações circulares são permitidas no python 3.5 (e provavelmente além), mas não 3.4 (e provavelmente abaixo).
Charlie Parker
4
Estou usando python 3.7.2 e ainda estou tendo um erro de tempo de execução devido a dependências circulares.
Richard Whitehead
Respostas:
281
Houve uma discussão muito boa sobre isso no comp.lang.python no ano passado. Responde bem à sua pergunta.
As importações são bem diretas, na verdade. Lembre-se do seguinte:
'import' e 'from xxx import yyy' são instruções executáveis. Eles são executados quando o programa em execução atinge essa linha.
Se um módulo não estiver em sys.modules, uma importação criará a nova entrada do módulo em sys.modules e, em seguida, executará o código no módulo. Ele não retorna o controle ao módulo de chamada até que a execução seja concluída.
Se um módulo existe em sys.modules, uma importação simplesmente retorna esse módulo, independentemente de ter ou não concluído a execução. Essa é a razão pela qual as importações cíclicas podem retornar módulos que parecem estar parcialmente vazios.
Por fim, o script em execução é executado em um módulo chamado __main__. A importação do script com seu próprio nome criará um novo módulo não relacionado a __main__.
Reúna esse lote e você não terá surpresas ao importar módulos.
@meawoppl Você poderia expandir esse comentário, por favor? Quão especificamente eles mudaram?
Dan Schien
3
A partir de agora, a única referência a importações circulares no python3 "O que há de novo?" páginas está no 3.5 . Ele diz "Importações circulares envolvendo importações relativas agora são suportadas". @meawoppl você encontrou mais alguma coisa que não está listada nestas páginas?
Zezollo
4
Eles são def. não suportado em 3.0-3.4. Ou pelo menos a semântica para o sucesso é diferente. Aqui está uma sinopse que descobri que não menciona as alterações 3.5. gist.github.com/datagrok/40bf84d5870c41a77dc6
meawoppl
Por favor, você pode expandir este "Finalmente, o script de execução é executado em um módulo chamado main , importando o script com seu próprio nome, criará um novo módulo não relacionado ao main .". Então, digamos que o arquivo é a.py e quando é executado como ponto de entrada principal, ele é o principal agora se tiver código como de uma variável de importação. Então o mesmo arquivo 'a.py' será carregado na tabela sys modules? Então, isso significa que, se houver uma declaração print, ela será executada duas vezes? Uma vez para o arquivo principal e novamente quando a importação é encontrada?
variável
Essa resposta é de 10 anos de idade, e eu gostaria de um update modernizado para garantir que ele permaneça correta em várias versões do Python, 2.x ou 3.x
Fallenreaper
296
Se você fizer por import foodentro bare por import bardentro foo, funcionará bem. Quando algo realmente for executado, os dois módulos estarão totalmente carregados e terão referências um ao outro.
O problema é quando você faz from foo import abce from bar import xyz. Porque agora cada módulo exige que o outro módulo já seja importado (para que o nome que estamos importando exista) antes que ele possa ser importado.
Parece que from foo import *e from bar import *também irá funcionar bem.
Akavall
11
Verifique a edição da postagem acima usando a.py/b.py. Ele não usa from x import y, e ainda assim recebe o erro circular de importação
Greg Ennis
2
Isso não é inteiramente verdade. Assim como import * from, se você tentar acessar um elemento na importação circular, no nível superior, portanto, antes de o script concluir sua execução, você terá o mesmo problema. Por exemplo, se você estiver configurando um pacote global em um pacote de outro e os dois se incluírem. Eu estava fazendo isso para criar uma fábrica desleixada para um objeto na classe base, onde esse objeto poderia ser uma de várias subclasses e o código de uso não precisava estar ciente de qual estava realmente criando.
AaronM
3
@ Akavall Na verdade não. Isso importará apenas os nomes disponíveis quando a importinstrução for executada. Portanto, não haverá erros, mas você pode não obter todas as variáveis que espera.
Augurar
3
Observe que, se você faz from foo import *e from bar import *, tudo o que fooé executado no está na fase de inicialização do bare as funções reais no barainda não foram definidas ...
Martian2049
100
As importações cíclicas terminam, mas você precisa ter cuidado para não usar os módulos importados ciclicamente durante a inicialização do módulo.
Considere os seguintes arquivos:
a.py:
print"a in"import sys
print"b imported: %s"%("b"in sys.modules,)import b
print"a out"
b.py:
print"b in"import a
print"b out"
x =3
Se você executar o a.py, obterá o seguinte:
$ python a.py
a in
b imported:False
b in
a in
b imported:True
a out
b out
a out
Na segunda importação de b.py (na segunda a in), o interpretador Python não importa bnovamente, porque já existe no dict do módulo.
Se você tentar acessar b.xa partir adurante a inicialização do módulo, você vai ter uma AttributeError.
Anexe a seguinte linha a a.py:
print b.x
Então, a saída é:
$ python a.py
a in
b imported:False
b in
a in
b imported:True
a out
Traceback(most recent call last):File"a.py", line 4,in<module>import b
File"/home/shlomme/tmp/x/b.py", line 2,in<module>import a
File"/home/shlomme/tmp/x/a.py", line 7,in<module>print b.x
AttributeError:'module' object has no attribute 'x'
Isso ocorre porque os módulos são executados na importação e, no momento em que b.xsão acessados, a linha x = 3ainda não foi executada, o que acontecerá somente depois b out.
isso explica muito o problema, mas e a solução? como poderíamos importar e imprimir corretamente x? a outra solução acima não funcionou para mim
Mehmet
Eu acho que essa resposta se beneficiaria muito se você usasse em __name__vez de 'a'. No começo, fiquei totalmente confuso por que um arquivo seria executado duas vezes.
Bergi 18/02
30
Como outras respostas descrevem esse padrão é aceitável em python:
def dostuff(self):from foo import bar
...
O que evitará a execução da instrução de importação quando o arquivo for importado por outros módulos. Somente se houver uma dependência circular lógica, isso falhará.
A maioria das importações circulares não é realmente importações circulares lógicas, mas gera ImportErrorerros, devido à maneira como import()avalia as instruções de nível superior de todo o arquivo quando chamado.
Isso ImportErrorsquase sempre pode ser evitado se você deseja positivamente suas importações no topo :
Considere esta importação circular:
App A
# profiles/serializers.pyfrom images.serializers importSimplifiedImageSerializerclassSimplifiedProfileSerializer(serializers.Serializer):
name = serializers.CharField()classProfileSerializer(SimplifiedProfileSerializer):
recent_images =SimplifiedImageSerializer(many=True)
App B
# images/serializers.pyfrom profiles.serializers importSimplifiedProfileSerializerclassSimplifiedImageSerializer(serializers.Serializer):
title = serializers.CharField()classImageSerializer(SimplifiedImageSerializer):
profile =SimplifiedProfileSerializer()
ImportError não é gerado se o módulo já tiver sido importado. Os módulos podem ser importados quantas vezes você quiser, por exemplo, "importar a; importar a;" está bem
Yuras 5/05
9
Eu tenho um exemplo aqui que me impressionou!
foo.py
import bar
class gX(object):
g =10
bar.py
from foo import gX
o = gX()
main.py
import foo
import bar
print"all done"
Na linha de comando: $ python main.py
Traceback(most recent call last):File"m.py", line 1,in<module>import foo
File"/home/xolve/foo.py", line 1,in<module>import bar
File"/home/xolve/bar.py", line 1,in<module>from foo import gX
ImportError: cannot import name gX
Como você resolveu isso? Eu estou tentando entender importação circular para corrigir um problema do meu próprio que a aparência muito semelhante ao que você está fazendo ...
c089
12
Errm ... acho que resolvi meu problema com esse hack incrivelmente feio. {{{se não for 'foo.bar' no sys.modules: da barra de importação foo else: bar = sys.modules ['foo.bar']}}} Pessoalmente, acho que as importações circulares são um sinal de aviso ENORME em código incorreto design ...
c089
5
@ c089, ou você pode apenas mover import barem foo.pyao fim
warvariuc
5
Se bare fooambos devem usar gX, a solução 'mais limpa' é colocar gXoutro módulo e ter ambos fooe barimportar esse módulo. (mais limpa no sentido de que não há são escondidos dependências semânticas.)
Tim Wilder
2
Tim tem um bom argumento. Basicamente, é porque barnem consegue encontrar gXno foo. a importação circular é boa por si só, mas não gXé definida quando é importada.
precisa saber é o seguinte
9
Módulo a.py:
import b
print("This is from module a")
Módulo b.py
import a
print("This is from module b")
A execução do "Módulo a" produzirá:
>>>'This is from module a''This is from module b''This is from module a'>>>
Ele produziu essas 3 linhas enquanto deveria produzir infinitival por causa da importação circular. O que acontece linha por linha durante a execução do "Módulo a" está listado aqui:
A primeira linha é import b. então vai visitar o módulo b
A primeira linha no módulo b é import a. então ele vai visitar o módulo
A primeira linha no módulo a é, import bmas observe que essa linha não será mais executada novamente , porque todo arquivo em python executa uma linha de importação apenas uma vez, não importa onde ou quando é executado. então ele passará para a próxima linha e será impresso "This is from module a".
Depois de terminar de visitar o módulo inteiro a do módulo b, ainda estamos no módulo b. então a próxima linha será impressa"This is from module b"
As linhas do módulo b são executadas completamente. então, voltaremos ao módulo a onde começamos o módulo b.
A linha import b já foi executada e não será executada novamente. a próxima linha será impressa "This is from module a"e o programa será concluído.
Concordo plenamente com a resposta do pitonista aqui. Mas encontrei algum código que era defeituoso com importações circulares e causava problemas ao tentar adicionar testes de unidade. Para corrigi-lo rapidamente sem alterar tudo, você pode resolver o problema fazendo uma importação dinâmica.
# Hack to import something without circular import issuedef load_module(name):"""Load module using imp.find_module"""
names = name.split(".")
path =Nonefor name in names:
f, path, info = imp.find_module(name, path)
path =[path]return imp.load_module(name, f, path[0], info)
constants = load_module("app.constants")
Novamente, essa não é uma correção permanente, mas pode ajudar alguém que deseja corrigir um erro de importação sem alterar muito o código.
Há muitas ótimas respostas aqui. Embora geralmente haja soluções rápidas para o problema, algumas das quais parecem mais pitônicas do que outras, se você tiver o luxo de refatorar, outra abordagem é analisar a organização do seu código e tentar remover a dependência circular. Você pode descobrir, por exemplo, que possui:
Arquivo a.py
from b import B
class A:@staticmethoddef save_result(result):print('save the result')@staticmethoddef do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))@staticmethoddef do_something_related_to_b(param):
B.do_something_b_ish(param)
Arquivo b.py
from a import A
class B:@staticmethoddef do_something_b_ish(param):
A.save_result(B.use_param_like_b_would(param))
Nesse caso, basta mover um método estático para um arquivo separado, diga c.py:
Arquivo c.py
def save_result(result):print('save the result')
permitirá remover o save_resultmétodo de A e, assim, remover a importação de A de a em b:
Arquivo refatorado a.py
from b import B
from c import save_result
class A:@staticmethoddef do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))@staticmethoddef do_something_related_to_b(param):
B.do_something_b_ish(param)
Arquivo refatorado b.py
from c import save_result
class B:@staticmethoddef do_something_b_ish(param):
save_result(B.use_param_like_b_would(param))
Em resumo, se você possui uma ferramenta (por exemplo, pylint ou PyCharm) que informa sobre métodos que podem ser estáticos, apenas lançar um staticmethoddecorador sobre eles pode não ser a melhor maneira de silenciar o aviso. Embora o método pareça relacionado à classe, pode ser melhor separá-lo, especialmente se você tiver vários módulos intimamente relacionados que possam precisar da mesma funcionalidade e pretender praticar os princípios DRY.
As importações circulares podem ser confusas porque a importação faz duas coisas:
executa código de módulo importado
adiciona módulo importado à tabela de símbolos globais do módulo importador
O primeiro é feito apenas uma vez, enquanto o último em cada declaração de importação. A importação circular cria situação quando o módulo de importação usa um importado com código parcialmente executado. Em conseqüência, ele não verá objetos criados após a declaração de importação. O exemplo de código abaixo demonstra isso.
As importações circulares não são o mal final a ser evitado a todo custo. Em algumas estruturas, como o Flask, elas são bastante naturais e ajustar seu código para eliminá-las não melhora o código.
main.py
print'import b'import b
print'a in globals() {}'.format('a'in globals())print'import a'import a
print'a in globals() {}'.format('a'in globals())if __name__ =='__main__':print'imports done'print'b has y {}, a is b.a {}'.format(hasattr(b,'y'), a is b.a)
b.by
print"b in, __name__ = {}".format(__name__)
x =3print'b imports a'import a
y =5print"b out"
a.py
print'a in, __name__ = {}'.format(__name__)print'a imports b'import b
print'b has x {}'.format(hasattr(b,'x'))print'b has y {}'.format(hasattr(b,'y'))print"a out"
saída main.py do python com comentários
import b
b in, __name__ = b # b code execution started
b imports a
a in, __name__ = a # a code execution started
a imports b # b code execution is already in progress
b has x True
b has y False# b defines y after a import,
a out
b out
a in globals()False# import only adds a to main global symbol table import a
a in globals()True
imports done
b has y True, a is b.a True# all b objects are available
Ok, acho que tenho uma solução bem legal. Digamos que você tenha arquivo ae arquivo b. Você tem um defou classno arquivo bque você deseja usar no módulo a, mas você tem algo mais, ou um def, classou variável do arquivo aque você precisa em sua definição ou classe no arquivo b. O que você pode fazer é, na parte inferior do arquivo a, depois de chamar a função ou classe no arquivo aque é necessário no arquivo b, mas antes de chamar a função ou classe do arquivo bque você precisa para o arquivo a, diga import b
Então, e aqui está a parte principal , em todas as definições ou classes no arquivo bque precisam do arquivo defou classdoa(vamos chamá-lo CLASS), você dizfrom a import CLASS
Isso funciona porque você pode importar o arquivo bsem o Python executar qualquer uma das instruções de importação no arquivo be, assim, evitar as importações circulares.
Por exemplo:
Arquivo a:
class A(object):def __init__(self, name):
self.name = name
CLASS = A("me")import b
go = B(6)
go.dostuff
Arquivo b:
class B(object):def __init__(self, number):
self.number = number
def dostuff(self):from a import CLASS
print"Hello "+ CLASS.name +", "+ str(number)+" is an interesting number."
from a import CLASSna verdade, não pula a execução de todo o código em a.py. É o que realmente acontece: (1) Todo o código no a.py é executado como um módulo especial "__main__". (2) Em import b, o código de nível superior em b.py é executado (definindo a classe B) e o controle retorna para "__main__". (3) "__main__" eventualmente passa o controle para go.dostuff(). (4) quando dostuff () chega import a, ele executa todo o código em a.py novamente , desta vez como o módulo "a"; depois importa o objeto CLASS do novo módulo "a". Então, na verdade, isso funcionaria igualmente bem se você usasse import aqualquer lugar do b.py.
Respostas:
Houve uma discussão muito boa sobre isso no comp.lang.python no ano passado. Responde bem à sua pergunta.
fonte
Se você fizer por
import foo
dentrobar
e porimport bar
dentrofoo
, funcionará bem. Quando algo realmente for executado, os dois módulos estarão totalmente carregados e terão referências um ao outro.O problema é quando você faz
from foo import abc
efrom bar import xyz
. Porque agora cada módulo exige que o outro módulo já seja importado (para que o nome que estamos importando exista) antes que ele possa ser importado.fonte
from foo import *
efrom bar import *
também irá funcionar bem.from x import y
, e ainda assim recebe o erro circular de importaçãoimport
instrução for executada. Portanto, não haverá erros, mas você pode não obter todas as variáveis que espera.from foo import *
efrom bar import *
, tudo o quefoo
é executado no está na fase de inicialização dobar
e as funções reais nobar
ainda não foram definidas ...As importações cíclicas terminam, mas você precisa ter cuidado para não usar os módulos importados ciclicamente durante a inicialização do módulo.
Considere os seguintes arquivos:
a.py:
b.py:
Se você executar o a.py, obterá o seguinte:
Na segunda importação de b.py (na segunda
a in
), o interpretador Python não importab
novamente, porque já existe no dict do módulo.Se você tentar acessar
b.x
a partira
durante a inicialização do módulo, você vai ter umaAttributeError
.Anexe a seguinte linha a
a.py
:Então, a saída é:
Isso ocorre porque os módulos são executados na importação e, no momento em que
b.x
são acessados, a linhax = 3
ainda não foi executada, o que acontecerá somente depoisb out
.fonte
__name__
vez de'a'
. No começo, fiquei totalmente confuso por que um arquivo seria executado duas vezes.Como outras respostas descrevem esse padrão é aceitável em python:
O que evitará a execução da instrução de importação quando o arquivo for importado por outros módulos. Somente se houver uma dependência circular lógica, isso falhará.
A maioria das importações circulares não é realmente importações circulares lógicas, mas gera
ImportError
erros, devido à maneira comoimport()
avalia as instruções de nível superior de todo o arquivo quando chamado.Isso
ImportErrors
quase sempre pode ser evitado se você deseja positivamente suas importações no topo :Considere esta importação circular:
App A
App B
De David Beazleys, excelente palestra Módulos e Pacotes: Live and Let Die! - PyCon 2015 ,
1:54:00
aqui está uma maneira de lidar com importações circulares em python:Isso tenta importar
SimplifiedImageSerializer
e, seImportError
for gerado, porque já foi importado, ele será puxado do importcache.PS: Você precisa ler este post inteiro na voz de David Beazley.
fonte
Eu tenho um exemplo aqui que me impressionou!
foo.py
bar.py
main.py
Na linha de comando: $ python main.py
fonte
import bar
emfoo.py
ao fimbar
efoo
ambos devem usargX
, a solução 'mais limpa' é colocargX
outro módulo e ter ambosfoo
ebar
importar esse módulo. (mais limpa no sentido de que não há são escondidos dependências semânticas.)bar
nem consegue encontrargX
no foo. a importação circular é boa por si só, mas nãogX
é definida quando é importada.Módulo a.py:
Módulo b.py
A execução do "Módulo a" produzirá:
Ele produziu essas 3 linhas enquanto deveria produzir infinitival por causa da importação circular. O que acontece linha por linha durante a execução do "Módulo a" está listado aqui:
import b
. então vai visitar o módulo bimport a
. então ele vai visitar o móduloimport b
mas observe que essa linha não será mais executada novamente , porque todo arquivo em python executa uma linha de importação apenas uma vez, não importa onde ou quando é executado. então ele passará para a próxima linha e será impresso"This is from module a"
."This is from module b"
"This is from module a"
e o programa será concluído.fonte
Concordo plenamente com a resposta do pitonista aqui. Mas encontrei algum código que era defeituoso com importações circulares e causava problemas ao tentar adicionar testes de unidade. Para corrigi-lo rapidamente sem alterar tudo, você pode resolver o problema fazendo uma importação dinâmica.
Novamente, essa não é uma correção permanente, mas pode ajudar alguém que deseja corrigir um erro de importação sem alterar muito o código.
Felicidades!
fonte
Há muitas ótimas respostas aqui. Embora geralmente haja soluções rápidas para o problema, algumas das quais parecem mais pitônicas do que outras, se você tiver o luxo de refatorar, outra abordagem é analisar a organização do seu código e tentar remover a dependência circular. Você pode descobrir, por exemplo, que possui:
Arquivo a.py
Arquivo b.py
Nesse caso, basta mover um método estático para um arquivo separado, diga
c.py
:Arquivo c.py
permitirá remover o
save_result
método de A e, assim, remover a importação de A de a em b:Arquivo refatorado a.py
Arquivo refatorado b.py
Em resumo, se você possui uma ferramenta (por exemplo, pylint ou PyCharm) que informa sobre métodos que podem ser estáticos, apenas lançar um
staticmethod
decorador sobre eles pode não ser a melhor maneira de silenciar o aviso. Embora o método pareça relacionado à classe, pode ser melhor separá-lo, especialmente se você tiver vários módulos intimamente relacionados que possam precisar da mesma funcionalidade e pretender praticar os princípios DRY.fonte
As importações circulares podem ser confusas porque a importação faz duas coisas:
O primeiro é feito apenas uma vez, enquanto o último em cada declaração de importação. A importação circular cria situação quando o módulo de importação usa um importado com código parcialmente executado. Em conseqüência, ele não verá objetos criados após a declaração de importação. O exemplo de código abaixo demonstra isso.
As importações circulares não são o mal final a ser evitado a todo custo. Em algumas estruturas, como o Flask, elas são bastante naturais e ajustar seu código para eliminá-las não melhora o código.
main.py
b.by
a.py
saída main.py do python com comentários
fonte
Resolvi o problema da seguinte maneira e funciona bem sem nenhum erro. Considere dois arquivos
a.py
eb.py
.Eu adicionei isso
a.py
e funcionou.a.py:
b.py:
A saída que recebo é
fonte
Ok, acho que tenho uma solução bem legal. Digamos que você tenha arquivo
a
e arquivob
. Você tem umdef
ouclass
no arquivob
que você deseja usar no móduloa
, mas você tem algo mais, ou umdef
,class
ou variável do arquivoa
que você precisa em sua definição ou classe no arquivob
. O que você pode fazer é, na parte inferior do arquivoa
, depois de chamar a função ou classe no arquivoa
que é necessário no arquivob
, mas antes de chamar a função ou classe do arquivob
que você precisa para o arquivoa
, digaimport b
Então, e aqui está a parte principal , em todas as definições ou classes no arquivob
que precisam do arquivodef
ouclass
doa
(vamos chamá-loCLASS
), você dizfrom a import CLASS
Isso funciona porque você pode importar o arquivo
b
sem o Python executar qualquer uma das instruções de importação no arquivob
e, assim, evitar as importações circulares.Por exemplo:
Arquivo a:
Arquivo b:
Voila.
fonte
from a import CLASS
na verdade, não pula a execução de todo o código em a.py. É o que realmente acontece: (1) Todo o código no a.py é executado como um módulo especial "__main__". (2) Emimport b
, o código de nível superior em b.py é executado (definindo a classe B) e o controle retorna para "__main__". (3) "__main__" eventualmente passa o controle parago.dostuff()
. (4) quando dostuff () chegaimport a
, ele executa todo o código em a.py novamente , desta vez como o módulo "a"; depois importa o objeto CLASS do novo módulo "a". Então, na verdade, isso funcionaria igualmente bem se você usasseimport a
qualquer lugar do b.py.