기본 지식.
Python typing이 무엇인지 모르시는 분들은 이 글을 읽어보시는 걸 추천합니다.
빙글빙글.
때는 신나게 Slack bot을 개발하던 시점이었습니다.1 복잡도가 상승하면서 별도 클래스나 모듈로 분리하는 요소가 많아지기 시작했죠. 이로 인해 typing도 점점 복잡해지기 시작했습니다. 대부분은 alias로 해결할 수 있지만, 순환 참조가 발생해버리니 머리가 아파지기 시작했습니다.
상황 1. 같은 모듈 내에서의 순환참조.
문제 설명.
from typing import List
class Teacher:
students: List[Student]
class Student:
teachers: List[Teacher]
실행하면 다음과 같은 상태가 됩니다.
Traceback (most recent call last):
File "typ.py", line 4, in <module>
class Teacher:
File "typ.py", line 6, in Teacher
students: List[Student]
NameError: name 'Student' is not defined
지금 저 코드 상태로는 정의 순서를 바꾼다고 해서 이 문제가 해결되진 않습니다.
해결책.
이럴땐 이렇게 해야합니다.
from typing import List
class Teacher:
students: List['Student']
class Student:
teachers: List[Teacher]
먼저 나온 Student를 문자열로 이름만 넣었습니다. 하지만 mypy는 이 정도만 해도 해당하는 이름을 찾아서 처리해줍니다.
상황 2. 다른 모듈 내에서의 순환참조.
상황 1은 간단히 해결할 수 있습니다. 하지만 서로 다른 모듈에 있는 상태라면 어떨까요?
문제 설명.
# mod1.py
from typing import List
from mod2 import Student
class School:
students: List[Student]
# mod2.py
from mod1 import School
class Student:
def register_school(school: School):
pass
mod2.py
에서 from mod1 import School
을 해버리는 경우 다음과 같은 상황이 벌어집니다.
Traceback (most recent call last):
File "mod1.py", line 3, in <module>
from mod2 import Student
File "/Users/item4/mod2.py", line 1, in <module>
from mod1 import School
File "/Users/item4/mod1.py", line 3, in <module>
from mod2 import Student
ImportError: cannot import name 'Student'
해결책.
이런 경우엔 이렇게 해야 합니다.
# mod2.py
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from mod1 import School
class Student:
def register_school(school: 'School'):
pass
typing 모듈의 TYPE_CHECKING
상수는 runtime에는 False
값을 갖고 있습니다.
mypy 등의 정적 타입 검사기를 돌릴 때만 True
가 됩니다.
이렇게 하면 순환 참조 문제를 해결할 수 있습니다.
각주.
-
YUI 라고 불리는 프로젝트인데 이건 기회가 올 때마다 하나씩 소개할 생각입니다. 원래 위치로