Programing

파이썬에서 순환 (또는 순환) 가져 오기

lottogame 2020. 3. 12. 08:09
반응형

파이썬에서 순환 (또는 순환) 가져 오기


두 모듈이 서로 가져 오면 어떻게됩니까?

문제를 일반화하기 위해 파이썬에서 주기적 가져 오기는 어떻습니까?


작년 comp.lang.python 에서 이것에 대해 정말 좋은 토론이 있었습니다. 그것은 당신의 질문에 아주 철저하게 대답합니다.

수입품은 정말 간단합니다. 다음을 기억하십시오.

'import'및 'from xxx import yyy'는 실행 문입니다. 실행중인 프로그램이 해당 라인에 도달하면 실행됩니다.

모듈이 sys.modules에 없으면 가져 오기는 sys.modules에 새 모듈 항목을 작성한 다음 모듈에서 코드를 실행합니다. 실행이 완료 될 때까지 호출 모듈로 제어를 되 돌리지 않습니다.

sys.modules에 모듈이 있으면 가져 오기는 실행이 완료되었는지 여부에 관계없이 해당 모듈을 반환합니다. 순환 가져 오기가 부분적으로 비어있는 모듈을 리턴 할 수있는 이유입니다.

마지막으로, 실행 스크립트는 __main__이라는 모듈에서 실행되며 자체 이름으로 스크립트를 가져 오면 __main__과 관련이없는 새 모듈이 생성됩니다.

그 많은 것을 함께 가져 가면 모듈을 가져올 때 놀랄 일이 없습니다.


import foo내부 barimport bar내부 를 수행 foo하면 제대로 작동합니다. 실제로 모든 것이 실행될 때까지 두 모듈이 완전히로드되고 서로 참조됩니다.

대신 당신이 할 때 문제는 from foo import abcfrom bar import xyz. 이제 각 모듈에서 다른 모듈을 가져와야합니다 (가져 오기 이름이 존재하도록).


주기적 가져 오기는 종료되지만 모듈 초기화 중에 주기적으로 가져온 모듈을 사용하지 않도록주의해야합니다.

다음 파일을 고려하십시오.

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

a.py를 실행하면 다음과 같은 결과가 나타납니다.

$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
b out
a out

b.py의 두 번째 가져 오기 (두 번째 a in)에서 Python 인터프리터는 b모듈 dict에 이미 존재하므로 다시 가져 오지 않습니다 .

액세스하려고하면 b.x에서 a모듈 초기화하는 동안, 당신은 얻을 것이다 AttributeError.

다음 줄을 추가하십시오 a.py.

print b.x

그런 다음 출력은 다음과 같습니다.

$ 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'

가져 오기시 모듈이 실행되고 b.x액세스 x = 3때 행 이 아직 실행되지 않았기 때문에이 작업은 이후에만 발생 b out합니다.


다른 답변에서 설명 하듯 이이 패턴은 파이썬에서 허용됩니다.

def dostuff(self):
     from foo import bar
     ...

다른 모듈에서 파일을 가져올 때 import 문이 실행되지 않습니다. 논리적 순환 종속성이있는 경우에만 실패합니다.

대부분의 순환 가져 오기는 실제로 논리적 순환 가져 오기가 아니라 호출 될 때 전체 파일의 최상위 문을 평가 ImportError하는 방식 때문에 오류를 발생시킵니다 import().

이들은 ImportErrors당신이 적극적으로 정상에 당신의 수입을 원하는 경우 거의 항상 피할 수 있습니다 :

이 순환 수입을 고려하십시오.

앱 A

# profiles/serializers.py

from images.serializers import SimplifiedImageSerializer

class SimplifiedProfileSerializer(serializers.Serializer):
    name = serializers.CharField()

class ProfileSerializer(SimplifiedProfileSerializer):
    recent_images = SimplifiedImageSerializer(many=True)

앱 B

# images/serializers.py

from profiles.serializers import SimplifiedProfileSerializer

class SimplifiedImageSerializer(serializers.Serializer):
    title = serializers.CharField()

class ImageSerializer(SimplifiedImageSerializer):
    profile = SimplifiedProfileSerializer()

David Beazleys의 뛰어난 대화 모듈 및 패키지 : Live and Let Die! -PyCon 2015 ,, 1:54:00python에서 순환 수입을 처리하는 방법은 다음과 같습니다.

try:
    from images.serializers import SimplifiedImageSerializer
except ImportError:
    import sys
    SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']

이 시도가 가져올 SimplifiedImageSerializer및 경우에 ImportError발생합니다 이미 수입되기 때문에, 그것은 importcache에서 그것을 가져옵니다.

추신 : David Beazley의 목소리로이 글 전체를 읽어야합니다.


나는 여기를 쳤다는 예를 얻었다!

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"

명령 행에서 : $ 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

모듈 a.py :

import b
print("This is from module a")

모듈 b.py

import a
print("This is from module b")

"모듈 a"를 실행하면 다음이 출력됩니다.

>>> 
'This is from module a'
'This is from module b'
'This is from module a'
>>> 

순환 가져 오기로 인해 무한을 출력 해야하는 동안이 3 줄을 출력합니다. "모듈 a"를 실행하는 동안 한 줄씩 수행되는 작업은 다음과 같습니다.

  1. 첫 번째 줄은 import b입니다. 모듈 b를 방문합니다
  2. 모듈 b의 첫 번째 줄은 import a입니다. 모듈 A를 방문합니다
  3. 모듈 a의 첫 번째 줄은 import b그러나이 줄은 더 이상 다시 실행되지 않습니다 . 파이썬의 모든 파일은 가져 오기 줄을 한 번만 실행하기 때문에 언제 어디서 실행되는지는 중요하지 않습니다. 다음 행으로 넘어 가서 인쇄 "This is from module a"합니다.
  4. 모듈 b에서 전체 모듈 a를 방문한 후에도 여전히 모듈 b에 있습니다. 그래서 다음 줄이 인쇄됩니다"This is from module b"
  5. 모듈 b 라인이 완전히 실행됩니다. 모듈 b를 시작한 모듈 a로 돌아갑니다.
  6. 가져 오기 b 행이 이미 실행되었으며 다시 실행되지 않습니다. 다음 줄이 인쇄 "This is from module a"되고 프로그램이 완료됩니다.

나는 pythoneer의 대답에 전적으로 동의합니다. 그러나 순환 가져 오기에 결함이 있고 단위 테스트를 추가하려고 할 때 문제가 발생하는 일부 코드를 발견했습니다. 모든 것을 변경하지 않고 빠르게 패치하려면 동적 가져 오기를 수행하여 문제를 해결할 수 있습니다.

# Hack to import something without circular import issue
def load_module(name):
    """Load module using imp.find_module"""
    names = name.split(".")
    path = None
    for 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")

다시 말하지만 이것은 영구적 수정은 아니지만 너무 많은 코드를 변경하지 않고 가져 오기 오류를 수정하려는 사람에게 도움이 될 수 있습니다.

건배!


가져 오기가 두 가지 작업을 수행하므로 순환 가져 오기가 혼동 될 수 있습니다.

  1. 가져온 모듈 코드를 실행합니다
  2. 가져 오기 모듈을 가져 오기 모듈 전역 기호 테이블에 추가

전자는 한 번만 수행되고 후자는 각 가져 오기 문에서 수행됩니다. 원형 가져 오기는 가져 오기 모듈이 부분적으로 실행 된 코드가있는 가져온 모듈을 사용할 때 상황을 만듭니다. 결과적으로 import 문 이후에 생성 된 객체는 보이지 않습니다. 아래 코드 샘플에서이를 보여줍니다.

순환 수입은 피해야 할 궁극적 인 악이 아니다. Flask와 같은 일부 프레임 워크에서는 매우 자연스럽고 코드를 수정하여 코드를 더 잘 만들지는 않습니다.

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 = 3
print 'b imports a'
import a
y = 5
print "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"

주석이있는 python main.py 출력

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

다음과 같은 방법으로 문제를 해결했으며 오류없이 잘 작동합니다. 두 파일을 고려 a.py하고 b.py.

나는 이것을 추가 a.py했고 효과가 있었다.

if __name__ == "__main__":
        main ()

a.py :

import b
y = 2
def main():
    print ("a out")
    print (b.x)

if __name__ == "__main__":
    main ()

b.py :

import a
print ("b out")
x = 3 + a.y

내가 얻는 결과는

>>> b out 
>>> a out 
>>> 5

여기에 많은 훌륭한 답변이 있습니다. 일반적으로 문제에 대한 빠른 해결책이 있지만 그중 일부는 다른 것보다 더 비판적이라고 생각하지만 리팩토링을하는 데 사치가 있다면 다른 방법은 코드의 구성을 분석하고 순환 종속성을 제거하는 것입니다. 예를 들어 다음과 같은 것을 발견 할 수 있습니다.

파일 a.py

from b import B

class A:
    @staticmethod
    def save_result(result):
        print('save the result')

    @staticmethod
    def do_something_a_ish(param):
        A.save_result(A.use_param_like_a_would(param))

    @staticmethod
    def do_something_related_to_b(param):
        B.do_something_b_ish(param)

파일 b.py

from a import A

class B:
    @staticmethod
    def do_something_b_ish(param):
        A.save_result(B.use_param_like_b_would(param))

이 경우 하나의 정적 메소드를 별도의 파일로 이동하면됩니다 c.py.

파일 c.py

def save_result(result):
    print('save the result')

save_resultA 에서 메소드를 제거 할 수 있으므로 b에서 a에서 A의 가져 오기를 제거 할 수 있습니다.

리팩토링 된 파일 a.py

from b import B
from c import save_result

class A:
    @staticmethod
    def do_something_a_ish(param):
        A.save_result(A.use_param_like_a_would(param))

    @staticmethod
    def do_something_related_to_b(param):
        B.do_something_b_ish(param)

리팩토링 된 파일 b.py

from c import save_result

class B:
    @staticmethod
    def do_something_b_ish(param):
        save_result(B.use_param_like_b_would(param))

요약하면 정적 일 수있는 메소드에 대해보고하는 도구 (예 : pylint 또는 PyCharm)가있는 경우 staticmethod데코레이터를 던지는 것이 경고를 끄는 가장 좋은 방법이 아닐 수 있습니다. 메소드가 클래스와 관련이있는 것처럼 보이지만, 특히 동일한 기능이 필요할 수있는 밀접하게 관련된 여러 모듈이 있고 DRY 원칙을 연습하려는 경우 메소드를 분리하는 것이 좋습니다.


이것은 다른 해결책 일 수 있으며 나를 위해 일했습니다.

def MandrillEmailOrderSerializer():
from sastaticketpk.apps.flights.api.v1.serializers import MandrillEmailOrderSerializer
return MandrillEmailOrderSerializer

email_data = MandrillEmailOrderSerializer()(order.booking).data

좋아, 나는 꽤 멋진 해결책이 있다고 생각한다. file a과 file 이 있다고 가정 해 봅시다 b. 당신은이 def또는를 class파일에 b이 모듈에 사용하려는 a, 그러나 당신은 다른 뭔가를 가지고 중 하나 def, class파일이나 변수 a는 파일에 사용자 정의 또는 클래스에 필요 b. 당신이 할 수있는 것은 파일의 맨 아래에,이다 a, 파일에있는 함수 또는 클래스 호출 한 후 a파일에 필요 b하지만 파일에서 함수 나 클래스를 호출하기 전에 b이 파일을 필요로 aimport b다음, 여기에 있습니다 중요한 부분 파일의 정의 나 모든 클래스에 b필요 def하거나 class파일을a(전화하자 CLASS)from a import CLASS

이것은 b파이썬이 file 에서 import 문을 실행하지 않고 파일 가져올 수 있기 때문에 b순환 가져 오기를 피할 수 있기 때문에 작동합니다.

예를 들면 다음과 같습니다.

파일 a :

class A(object):

     def __init__(self, name):

         self.name = name

CLASS = A("me")

import b

go = B(6)

go.dostuff

파일 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."

짜잔

참고 URL : https://stackoverflow.com/questions/744373/circular-or-cyclic-imports-in-python

반응형