Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

Python 클래스 간 순환 종속성(Circular Dependency) 처리 방법

문제 상황: 순환 종속성이 있는 코드

다음 코드는 두 Python 클래스 사이에 순환 종속성(circular dependency)이 존재하는 경우를 보여줍니다.

class P():
    q = Q().do_something()
    def do_something(self):
        pass

class Q():
    p = P().do_something()
    def do_something(self):
        pass

이 코드를 실행하면 NameError가 발생합니다. 그 이유는 무엇일까요?

왜 이런 문제가 발생할까?

함수와 달리 클래스는 정의되는 시점에 클래스 본문이 즉시 실행됩니다. 따라서 클래스 P를 정의하려면 Q의 메서드를 실제로 호출해야 하는데, 아직 클래스 Q가 정의되기 전이기 때문에 이 호출이 불가능합니다. 반대 방향(Q가 P를 참조)에서도 같은 문제가 발생합니다.

즉, 클래스 수준의 구문(q = Q.do_something())은 인스턴스를 생성할 때가 아니라 클래스를 정의하는 순간 실행되기 때문에, 서로를 참조하는 두 클래스는 어느 쪽을 먼저 정의해도 오류가 날 수밖에 없습니다.

해결 방법: 클래스 정의 후 속성 추가하기

이 문제를 우회하는 가장 간단한 방법은, 먼저 두 클래스의 뼈대만 정의한 뒤 나중에 속성을 추가하는 것입니다.

class P(object):
    pass

class Q(object):
    pass

P.q = Q.do_something()
Q.p = P.do_something()

두 클래스가 모두 정의된 상태에서 속성을 동적으로 할당하므로, 순환 참조 문제 없이 코드가 정상적으로 동작합니다.

이 방법의 한계

그러나 만약 각 do_something() 호출이 상대방의 호출이 이미 실행된 결과에 의존한다면, 위 방법만으로는 충분하지 않습니다. 이 경우에는 다음과 같은 추가적인 접근이 필요합니다.

  • 지연 초기화(Lazy Initialization): 속성을 클래스 수준이 아니라 메서드나 생성자(__init__) 안에서 필요한 시점에 할당합니다.
  • 참조 분리: 한쪽 클래스가 다른 쪽을 직접 참조하지 않도록 공통 로직을 제3의 모듈이나 클래스로 분리합니다.

마무리

두 클래스가 모두 정의된 이후에 클래스의 정의를 완성할 수 있다는 점은 Python이 동적 언어(dynamic language)라는 사실을 잘 보여주는 예입니다. 클래스 몸체가 정의 시점에 실행된다는 특성을 이해하고 있으면, 이런 순환 종속성 문제를 훨씬 쉽게 진단하고 해결할 수 있습니다.