Programing

Python의 순환 가져 오기 종속성

crosscheck 2020. 10. 28. 07:44
반응형

Python의 순환 가져 오기 종속성


다음 디렉터리 구조가 있다고 가정 해 보겠습니다.

a\
    __init__.py
    b\
        __init__.py
        c\
            __init__.py
            c_file.py
        d\
            __init__.py
            d_file.py

에서 a패키지의 __init__.pyc패키지를 가져옵니다. 하지만 c_file.py수입 a.b.d.

이 프로그램은 말을, 실패 b할 때 존재하지 않는 c_file.py시도 가져올 a.b.d. (그리고 우리가 가져 오는 중이었기 때문에 실제로 존재하지 않습니다.)

이 문제를 어떻게 해결할 수 있습니까?


a가 c에 의존하고 c가 a에 의존한다면, 그들은 실제로 동일한 단위입니까?

a와 c를 두 개의 패키지로 분리 한 이유를 실제로 조사해야합니다. 일부 코드를 다른 패키지로 분리해야하기 때문입니다 (둘 다 새 패키지에 종속되지만 서로가 아닌). 하나의 패키지로.


다음과 같이 가져 오기를 연기 할 수 있습니다 a/__init__.py.

def my_function():
    from a.b.c import Blah
    return Blah()

즉, 실제로 필요할 때까지 가져 오기를 연기합니다. 그러나 지적 된 것과 같은 순환 종속성이 설계 문제를 나타낼 수 있으므로 패키지 정의 / 용도를 자세히 살펴볼 것입니다.


나는 이것을 몇 번 궁금해했다 (보통 서로에 대해 알아야하는 모델을 다룰 때). 간단한 해결책은 전체 모듈을 가져온 다음 필요한 것을 참조하는 것입니다.

그래서하는 대신

from models import Student

하나에

from models import Classroom

다른 하나는 그냥

import models

그런 다음 필요할 때 models.Classroom을 호출하십시오.


문제는 디렉토리에서 실행할 때 기본적으로 하위 디렉토리 인 패키지 만 후보 가져 오기로 표시되므로 abd를 가져올 수 없다는 것입니다. 그러나 b는 a의 하위 패키지이므로 bd를 가져올 수 있습니다.

정말로 abd를 가져오고 싶다면 c/__init__.py시스템 경로를 a 위의 디렉토리로 변경하고 가져 오기를 a/__init__.pyimport abc 로 변경하여이를 수행 할 수 있습니다.

귀하는 a/__init__.py다음과 같아야합니다 :

import sys
import os
# set sytem path to be directory above so that a can be a 
# package namespace
DIRECTORY_SCRIPT = os.path.dirname(os.path.realpath(__file__)) 
sys.path.insert(0,DIRECTORY_SCRIPT+"/..")
import a.b.c

c에서 모듈을 스크립트로 실행하려는 경우 추가 어려움이 발생합니다. 여기서 패키지 a와 b는 존재하지 않습니다. __int__.pyc 디렉토리를 해킹하여 sys.path가 최상위 디렉토리를 가리 키도록 한 다음 __init__c 내부의 모든 모듈을 가져 와서 전체 경로를 사용하여 abd를 가져올 __init__.py수 있습니다. 내 사용 사례에서 작동했습니다.


다음과 같은 패턴을 제안합니다. 이를 사용하면 자동 완성 및 유형 힌트가 제대로 작동 할 수 있습니다.

cyclic_import_a.py

import playground.cyclic_import_b

class A(object):
    def __init__(self):
        pass

    def print_a(self):
        print('a')

if __name__ == '__main__':
    a = A()
    a.print_a()

    b = playground.cyclic_import_b.B(a)
    b.print_b()

cyclic_import_b.py

import playground.cyclic_import_a

class B(object):
    def __init__(self, a):
        self.a: playground.cyclic_import_a.A = a

    def print_b(self):
        print('b1-----------------')
        self.a.print_a()
        print('b2-----------------')

이 구문을 사용하여 클래스 A 및 B를 가져올 수 없습니다.

from playgroud.cyclic_import_a import A
from playground.cyclic_import_b import B

You cannot declare the type of parameter a in class B __ init __ method, but you can "cast" it this way:

def __init__(self, a):
    self.a: playground.cyclic_import_a.A = a

Another solution is to use a proxy for the d_file.

For example, let's say that you want to share the blah class with the c_file. The d_file thus contains:

class blah:
    def __init__(self):
        print("blah")

Here is what you enter in c_file.py:

# do not import the d_file ! 
# instead, use a place holder for the proxy of d_file
# it will be set by a's __init__.py after imports are done
d_file = None 

def c_blah(): # a function that calls d_file's blah
    d_file.blah()

And in a's init.py:

from b.c import c_file
from b.d import d_file

class Proxy(object): # module proxy
    pass
d_file_proxy = Proxy()
# now you need to explicitly list the class(es) exposed by d_file
d_file_proxy.blah = d_file.blah 
# finally, share the proxy with c_file
c_file.d_file = d_file_proxy

# c_file is now able to call d_file.blah
c_file.c_blah() 

참고URL : https://stackoverflow.com/questions/1556387/circular-import-dependency-in-python

반응형