Python static type checker
by Understand이번에 python 개발 환경을 처음부터 설정해야 하는 일이 생겼다. 이때 기존에 프로젝트를 하던 관성처럼 static type checker를 활용하고자 했는데, 생각보다 종류가 있었다. 여러 static type checker에서 의사 결정을 하기 위해 python에서의 static type checker를 조사해보았다
Python에서 Static type checker를 사용하는 이유
파이썬은 C나 JAVA와 같은 정적 타입 언어와 다르게 코드를 작성할 때 각 변수의 타입을 지정하지 않아도 되는 동적 타입 언어이다. 하지만 내가 속해있던 팀은 다음과 같은 이유로 type hint를 사용하고 있다.
- 버그를 줄이기 위해 = Type으로 인한 Side effect를 줄이기 위해
- Data의 type을 빠르게 파악해 생산성을 높이기 위해
하지만 Python 에서는 type 검사를 하지 않기 때문에 type hint만으로는 type으로 인한 버그를 찾기 어렵다. 따라서 잘못된 type이 사용된 경우를 찾기 위해 static type checker라는 툴을 사용하게 된다. 유명한 python 웹프레임워크인 Django, Flask, FastAPI 나 딥러닝, 데이터 관련 라이브러리인 pytorch, numpy 도 모두 static type checker를 사용한다.
대부분의 오픈소스들은 static type checker 툴로 mypy를 사용하고 있고, 그중의 일부는 pyright나 pytype 등의 라이브러리를 사용하고 있다.
이 글에서는 여러 static type checker 중 mypy와 pyright의 특징과 장단점을 비교해보고자 한다.
- 참고) 현재는 pyright의 문서를 많이 참고하였기 때문에 pyright에 유리하게 작성되어 있는 부분이 많다.
Mypy
Mypy는 최초이자 가장 많이 사용되고 있는 python static type checker로 Jukka Lehtosalo가 케임브리지 박사과정에서 개발했다. 2012년 pycon에 발표된 내용에 따르면 mypy는 Typescript와 같이 python에 타입을 추가한 새로운 언어로 설계하고자 했다. 이후 2014년 경 python 창시자인 Guido van Rossum의 제안에 따라 type hint를 사용해 타입을 선택적으로 검사할 수 있도록 재작성되었다.
Mypy는 파이썬 정적 타입 표준을 정의하는 PEP 484에 맞추어 검사를 하게 된다.
Pyright
Pyright는 microsoft에서 개발한 static type checker이다. 기존 mypy의 속도가 매우 느렸기 때문에 속도를 개선하는 설계를 중시하고 있다. 또한 PEP 484 뿐만 아니라 이후에 나온 Typing PEP들 (PEP 526, PEP 544, PEP 586, PEP 589 등)에 대해서도 검사한다는 특징이 있다.
Mypy vs Pyright
검사 속도 : 약 5000~10000라인 정도의 코드베이스에서 검사 속도를 비교했을 떄는 mypy와 pyright의 차이가 크지 않았다.
오히려 mypy는 caching을 사용해 두번째 검사부터는 빠른 검사 속도를 보여준다. pyright는 타입 추론 방식으로 인해 caching으로 인한 속도가 크게 체감되지 않았다.
mypy와 pyright의 기능: 기본적으로 pyright가 더 많은 기능을 커버하며 더 안정적인 것으로 보인다.
관련된 내용이 Pyright의 문서에 잘 작성되어 이를 간단하게 정리해보았다.
1. Type Hint가 없는 코드에 대한 검사 유무
Mypy는 type hint가 없는 코드에 대해서는 type검사를 하지 않는다. Jukkal에 따르면 크게 3가지 이유에서 이러한 정책을 만들었다고 한다.
- mypy의 목적에는 코드를 이해하기 쉽게 만드는 것도 포함되어 있다. type hint가 없는 코드에 대해서도 타입 검사를 하게 되면 오히려 사용자가 type hint를 작성하지 않아 가독성이 떨어질 것이라고 말한다.
- Type 검사를 하는 것과 하지 않는 것이 명시적으로 구별되는 것이 좋다. Type hint가 없는 코드 중 일부는 타입을 유추할 수 있고 일부는 타입을 유추할 수 없다. 만약 type을 유추해서 검사를 하게 된다면 모듈의 어떤 부분이 type 검사되고 있는지, 어떤 부분이 안되고 있는지 구별하기 어려울 것이다.
- 기술적으로도 type hint가 없는 코드에 대해서는 타입 검사를 하지 않는 것이 더 쉽다.
Pyright는 type hint가 없어도 return type을 추론하여 검사한다. Type 검사를 하는 것과 하지 않는 것이 명시적으로 구별
하기 위해서 명시적 형식 Any와 암시적 형식을 구분하기 위해 Unknown 이라는 타입을 사용한다.
2. Union Type에 대한 처리
Union[A, B]라는 타입을 검사할 떄 pyright는 A 타입과 B 타입 둘 모두를 고려해서 처리한다(A or B). 하지만 mypy의 경우 A와 B의 공통 상위 타입으로 처리한다. mypy의 방식으로 인해 많은 flase-postive에러가 발생하게 된다.
3. 동작의 일관성
Mypy는 type check시 몇가지 부분에서 일관적이지 않게 처리한다. 반면 pyright은 좀더 일관적으로 처리한다.
아래 예시는 mypy 1.0.1, pyright 1.1.295 버전에서 직접 시행해보았다.
Example 1) 변수 타입 추론
pyright는 둘 모두 error가 발생하지 않는다. 반면 mypy는 func 1에서는 error가 발생하지만 func 2에서는 error가 발생하지 않는다.
def func1(condition: bool):
if condition:
x = 3
else:
x = ""
def func2(condition: bool):
x = None
if condition:
x = ""
Example 2) 변수 할당시 type 축소 (type narrowing)
Pyright는 a와 b의 타입이 list[Any]로 동일하다. 하지만 mypy의 경우 a는 list[Any], b는 list[int]로 타입이 축소된다.
b: list[Any]
b = [1, 2, 3]
reveal_type(b) # pyright: list[Any], mypy: list[Any]
c = [1, 2, 3]
b = c
reveal_type(b) # pyright: list[Any], mypy: list[int]
4. 기타 버그
mypy는 type check시 몇가지 버그가 존재한다.
Example 1) 클래스 및 인스턴스 변수 구분
pyright는 z가 인스턴스 변수임을 구분하고 에러를 출력한다. 하지만 mypy는 에러 메시지를 출력하지 않는다.
class A:
x: int = 0 # Regular class variable
y: ClassVar[int] = 0 # Pure class variable
def __init__(self):
self.z = 0 # Pure instance variable
print(A.x)
print(A.y)
print(A.z) # pyright: error, mypy: no error
Example 2) Import 로딩 매커니즘
pyright는 위 경우 에러메시지를 출력한다. 반면 mypy는 에러메시지를 출력하지 않는다.
import collections.abc
collections.deque()
이 외에도 다양한 경우에 있어 pyright가 좀 더 일관적이고 false alarm이 적은 것 같다.
물론 순환참조와 같이 mypy는 가능하지만 pyright은 불가능한 경우도 있다. (다만, TypeVar를 사용한 Generic type에 대해서는 두 도구 모두 타입을 완벽히 검사하지 못한다.)
정리
Mypy는 다양한 python버전과 IDE에 모두 호환이 된다는 장점과 caching으로 인해 빠르게 검사할 수 있다는 장점이 있다. 하지만 false-alarm이 매우 많아 유저들 사이에서도 불만이 많아 mypy를 위한 가이드라인(mypy-guide, the-missing-guide-to-python-static-typing)을 작성해준 유저들도 있다.
Pyright는 python 3.5 이상에서부터 동작하며 마찬가지로 많은 IDE에서 동작하며, 특히 visual studio code와 궁합이 좋다. 또한 상대적으로 일관적이고 많은 영역에 대해 검사를 해준다. 하지만 아직 mypy에 비해 프로젝트에 적용된 사례가 적고, (내가 느끼기엔) 속도면에서 큰 강점을 보이진 않았다.
Reference
블로그의 정보
BookStoreDiary
Understand