source

Python에서 상호 또는 순환(순환) 가져오기를 사용하면 어떻게 됩니까?

gigabyte 2022. 9. 12. 11:41
반응형

Python에서 상호 또는 순환(순환) 가져오기를 사용하면 어떻게 됩니까?

이 Python을 ?import서로?보다 일반적으로는 여러 모듈이 다음을 시도하면 어떻게 됩니까?import★★★★★★★★★★★★★★★★★★?


자세한 내용은 "ImportError: Cannot import name X" 또는 "AttributeError: ..."(순환 Import로 인해 발생할 있음)를 참조하십시오.발생할 수 있는 일반적인 문제에 대한 설명 및 이러한 Import를 피하기 위한 코드 재작성 방법에 대한 조언.순환 Import가 콜스택에서 위쪽에서 기능하는 것처럼 보이지만 ImportError가 더 아래쪽에서 발생하는 이유는 무엇입니까?를 참조하십시오.문제의 발생 원인과 발생 방법에 대한 자세한 내용은 를 참조하십시오.

하면.import foo (비밀(이행)bar.py 및 )의 개요import bar (비밀(이행)foo.py정상적으로 동작합니다.어떤 것이 실제로 실행될 때까지 두 모듈은 완전히 로딩되어 서로 참조할 수 있습니다.

는 ' 때'를 할 때 입니다.from foo import abc (비밀(이행)bar.py 및 )의 개요from bar import xyz(비활성화(안에)foo.py왜냐하면 이제 각 모듈이 필요하기 전에 가져올 수 있게 해). 다른 모듈이 이미(우리가 수입하고 있는 이름 존재하도록)가 수입되는 것.각 모듈을 Import하기 전에 다른 모듈을 Import(Import하는 이름이 존재하도록)해야하기 때문입니다.

작년에 comp.lang.python에서 정말 좋은 토론이 있었어요.당신의 질문에 아주 잘 대답해주네요.

수입은 정말 간단하다.다음 사항에 주의해 주십시오.

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

모듈이 sys.modules에 없는 경우 Import에 의해 sys.modules에 새로운 모듈엔트리가 생성되어 모듈의 코드가 실행됩니다.실행이 완료될 때까지 콜링 모듈로 제어가 반환되지 않습니다.

모듈이 sys.modules에 존재하는 경우 Import는 단순히 실행 완료 여부에 관계없이 해당 모듈을 반환합니다.그렇기 때문에 주기적인 Import는 부분적으로 비어 있는 것처럼 보이는 모듈을 반환할 수 있습니다.

마지막으로 실행 중인 스크립트는 __main__라는 이름의 모듈에서 실행되며 스크립트를 자체 이름으로 Import하면 __main_과(와) 관련이 없는 새로운 모듈이 생성됩니다.

모듈을 Import 할 때 놀라지 않도록 이 제품들을 함께 보관하십시오.

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

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

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(두번째b.py의 두에 번째 Import시(두 번째 Import시)의 두번째의 용병에.a in), 파이선 인터프리터 인터프리터는 Import하지 않습니다 파이선은 가져오지 않습니다.b다시, 왜냐하면 그것은 이미 모듈dict.module 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.

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

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

그러면 다른 모듈에 의해 파일이 Import될 때 Import 문이 실행되지 않습니다.논리적 순환 종속성이 있는 경우에만 실패합니다.

대부분의 원형은 수입이 실제로 원형 수입 아니라 순환 순환 대부분의 Import는 실제로는 논리적 Import가 아니라오히려 인상입니다를 논리적이지 않고 있다.ImportError방식 때문에 오류 에러,그 이유는.import()는 호출 시 파일 전체의 최상위 스테이트먼트를 평가합니다.

Import를 확실히 선두에 두고 싶은 경우는, 거의 항상 이러한 문제를 회피할 수 있습니다.

다음 순환 가져오기를 고려하십시오.

앱 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 Beazley의 훌륭한 토크 모듈패키지: Live and Let Die! - PyCon 2015,1:54:00비단뱀의 python 서를를를 Import 。

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

은 Import를 합니다.SimplifiedImageSerializer, 「」의 경우는,ImportError수입하다

추신: 이 글 전체를 데이비드 비즐리의 목소리로 읽어야 합니다.

모듈 a.py:

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

모듈 b.py

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

"Module a"를 실행하면 다음과 같이 출력됩니다.

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

순환 Import로 인해 무한출력을 하기로 되어 있었는데 이 3줄을 출력했습니다."Module a" 실행 중 행별로 수행되는 작업을 다음에 나타냅니다.

  1. 번째 은 " " " 입니다.import b b를
  2. 의 첫 은 "b" 입니다.import a a에
  3. 의 첫 은 "a" 입니다.import b그러나 python 내의 모든 파일은 Import 행을 한 번만 실행하므로 이 행은 더 이상 실행되지 않습니다.어디서 실행되든 언제 실행되든 상관없습니다.그래서 다음 행으로 넘어가서 인쇄를 합니다."This is from module a".
  4. 은 "b"로 됩니다."This is from module b"
  5. 모듈 b 라인은 완전히 실행되므로 모듈 b를 시작한 모듈a로 돌아갑니다
  6. b가져오면 다음 ."This is from module a"프로그램이 종료됩니다.

놀랍게도, 타입 힌트에 의한 주기적인 수입에 대해서는 아직 아무도 언급하지 않았습니다.
타입 힌트의 결과로서만 주기적인 Import가 있는 경우, 그것들을 깔끔하게 회피할 수 있습니다.

고려하다를 고려해 보세요main.py어느 다른 파일에서 예외를 사용한다.다른파일의 예외를 사용합니다.

from src.exceptions import SpecificException

class Foo:
    def __init__(self, attrib: int):
        self.attrib = attrib

raise SpecificException(Foo(5))

그리고 그 헌신적인 예외 클래스 그리고전용 예외 클래스.exceptions.py:

from src.main import Foo

class SpecificException(Exception):
    def __init__(self, cause: Foo):
        self.cause = cause

    def __str__(self):
        return f'Expected 3 but got {self.cause.attrib}.'

이 하찮게 한이렇게 하면 문제가 생깁니다를 높일 것이다.ImportError~하듯이로main.py수입 Imports(수입)exception.py그리고 반대로를통해그 반대도 마찬가지입니다를 통해.Foo ★★★★★★★★★★★★★★★★★」SpecificException.

왜냐하면 왜냐면Foo오직에서만 필요합니다에 필요합니다.exceptions.py형식 시험하는 동안, 우리가 안전하게는 수입 조건은형식 검사 중에, 우리는 안전하게 그것을 조건부로 만들 수 있습니다를 사용하여 변경할 수 있다.TYPE_CHECKING입력 모듈에서 상수입니다.이 상수는True우리에게 조건부로형식검사 중, 조건부로 Import할수 있습니다를 가져올 수 있는 동안 형식의 당좌.Foo그리고 거기 그 원형으로 수입 오류를 피한다.따라서 순환 수입방지할 수 있습니다 오류를.
파이선 3.6, 전방 참조를 사용하여:.Python 3.6 Forward Reference 。

from typing import TYPE_CHECKING
if TYPE_CHECKING:  # Only imports the below statements during type checking
   ​from src.main import Foo

class SpecificException(Exception):
   ​def __init__(self, cause: 'Foo'):  # The quotes make Foo a forward reference
       ​self.cause = cause

   ​def __str__(self):
       ​return f'Expected 3 but got {self.cause.attrib}.'

Python 3.7+에서는 주석 평가(PE 563에서 도입)를 연기하면 다음 참조 대신 '정상' 유형을 사용할 수 있습니다.

from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:  # Only imports the below statements during type checking
   ​from src.main import Foo

class SpecificException(Exception):
   ​def __init__(self, cause: Foo):  # Foo can be used in type hints without issue
       ​self.cause = cause

   ​def __str__(self):
       ​return f'Expected 3 but got {self.cause.attrib}.'

3. Python 3.11+의 from __future__ import annotations는 디폴트로 액티브하기 때문에 생략할 수 있습니다.

이 답변은 Stefaan Lippens가 Python의 원형 Import 홀에서 당신을 꺼내주는 Yet another 솔루션에 기초하고 있습니다.

여기 문득 떠오른 예가 있어요!

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.화이

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

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로부터의 Import를 삭제할 수 있습니다.

리팩터링된 파일 a.py

from b import B
from c import save_result

class A:
    @staticmethod
    def do_something_a_ish(param):
        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))

스태틱할 이 있는 PyCharm, 「」( 「: pylint」 「PyCharm」)을만 하면 됩니다.staticmethod경고를 잠재우는 최선의 방법은 아닐 수도 있습니다.이 메서드가 클래스에 관련된 것처럼 보이지만, 특히 동일한 기능을 필요로 하는 밀접하게 관련된 모듈이 여러 개 있고 DRY 원칙을 실천하려는 경우에는 이 메서드를 분리하는 것이 좋습니다.

나는 여기서 피토네이터의 대답에 전적으로 동의한다.그러나 순환수입에 결함이 있어 유닛 테스트를 추가하려고 할 때 문제가 발생한 코드를 우연히 발견했습니다.따라서 모든 것을 변경하지 않고 신속하게 패치를 적용하려면 동적 가져오기를 수행하여 문제를 해결할 수 있습니다.

# 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")

이것은 영구적인 수정은 아니지만 코드를 너무 많이 변경하지 않고 Import 오류를 수정하려는 사용자에게 도움이 될 수 있습니다.

건배!

가져오기는 다음 두 가지 작업을 수행하므로 순환 가져오기는 혼란스러울 수 있습니다.

  1. Import된 모듈코드를 실행합니다.
  2. Import된 모듈을 Import 모듈글로벌 심볼테이블에 추가

전자는 한 번만 수행되고 후자는 각 수입 명세서에서 수행됩니다.순환 Import는 Import 모듈이 부분적으로 실행된 코드를 가진 Import된 모듈을 사용하는 상황을 만듭니다.따라서 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

python이라는 이름의 합니다.request.pypy에서는 writerequest.py 라고 .

import request

그래서 이 또한 순환 수입일 가능성이 높습니다.

솔루션:

됩니다.aaa.py, 외외에request.py.

다른 libs에서 이미 사용하고 있는 이름은 사용하지 마십시오.

저는 다음과 같이 문제를 해결했고, 오류 없이 잘 작동합니다.의 파일을 a.py ★★★★★★★★★★★★★★★★★」b.py.

을 to거거에 했습니다.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 파일 " " "b가 있습니다.def ★★★classba 또 게 def,class 파일로부터의 a 내의 한 것b할 수 것은 맨 a의 또는 「 」a입니다.b, 파일 "에서하기 전b한 " " " " " "a를 들어맞다import b다음은 파일 내의 모든 정의 또는 클래스의 주요 부분입니다.b은, 필, that, that이 필요한 경우def ★★★★★★★★★★★★★★★★★」class 「」에서a)CLASS.from a import CLASS

은 Import 파일을 할 수 합니다.b Python의 하지 않고 .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."

보일라.

Python에서 모듈을 사용하는 것을 매우 좋아합니다.모듈은 추가 기능, 더 나은 코딩 관행, 그리고 항상 사용하는 동안 적절한 구조를 만듭니다.그러나 실수로 모듈 이름으로 명명된 다른 파일이 있을 경우 python 순환 Import 문제가 발생할 수 있습니다.python은 먼저 로컬 현재 디렉토리에서 가져오기를 선호하고 다음으로 사이트 패키지에서 가져오기를 선호하기 때문에 순환 Import 문제가 발생합니다.

일반적으로 Python Circular Import 문제는 작업 파일의 이름을 모듈 이름과 동일하게 지정하고 이들 모듈이 서로 의존할 때 발생합니다.이렇게 하면 python은 같은 파일을 열어서 순환 루프를 일으키고 최종적으로 오류를 발생시킵니다.

예를 들어 파일 이름을 random.py로 지정하고 "from random import randint"를 Import하려고 하면 순환 Import 오류(부분 초기화 모듈에서라고도 함)가 발생합니다.

언급URL : https://stackoverflow.com/questions/744373/what-happens-when-using-mutual-or-circular-cyclic-imports-in-python

반응형