프로그래밍 언어의 기본 타입 또는 개발자가 정의한 타입을 기반으로 해당 타입을 언어와 연관시키는 메커니즘을 뜻한다.
타입 동등, 타입 호환, 타입 추론에 대한 규칙을 지킨다면 타입시스템이라고 볼 수 있다.
모든 언어는 이러한 타입시스템을 가지고 있으며 동적 타입과 정적 타입으로 나눠진다
최근에는 파이썬이나 자바스크립트 같은 동적 타입 언어로 개발할 때도 정적 타입 언어의 특성들을 활용하여 시스템의 안정성을 개선하는 개발 방식이 일반화 되고 있다.
파이썬의 타입 힌트는 파이썬 코드에 정적 타입을 선택적으로 제공하는 기능이다.
타입 힌트를 추가하면 IDE로 코딩할 때 잘못된 타입을 사용할 경우에 경고 표시가 뜨는 것 같은 기능을 활용할 수 있다. 현재 대부분의 IDE에서 타입 힌트를 지원하고 있다.
모든 부분에 타입 힌트를 적용할 필요는 없지만, 타입 힌트가 많을 수록 정적 코드 분석도구(mypy 등)를 통해 프로그램의 잠재적 버그를 찾아낼 수 있는 정보도 많아진다.
타입 힌트를 위하여 파이썬 3.5버전부터 typing
이란 내장 모듈을 지원한다.
타입 힌트 예제 코드
from typing import List, Set, Dict, Tuple, Optional, Union
# 변수
my_number: int = 777
my_string: str = "foo"
foo: float # 타입 힌트가 있다면 변수에 값을 할당하지 않아도 괜찮다
foo = 3.14
# 함수 매개변수와 리턴 값
def add(a: int, b: int) -> int:
return a + b
# 다중 타입 설정
spam: Union[int, str, float] = 42
spam = "hello"
spam = 3.14
# 파이썬 3.10 부터는 다음과 같은 코드도 가능하다.
spam: int | str | float = "spam"
# None과 다중 타입 설정
last_name: Optional[str] = None
last_name = "Kim"
# 모든 데이터 타입이 가능한 Any
spam: Any = 42
spam = "spam"
spam = True
# 내부 값을 정하지 않은 리스트
spam: list = [1, 2, 3, 'foo', 3.14, True]
# 내부 값을 구체적으로 선언한 리스트
cat_name: List[str] = ["simon", "garfield", "chester"]
numbers: List[Union[int, float]] = [42, 3.14, 99.9, 87]
# https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html#built-in-types
# 간단한 파이썬 빌트인 타입들은 임포트할 필요가 없고 그냥 사용하면 된다
x: int = 1
x: float = 1.0
x: bool = True
x: str = "test"
x: bytes = b"test"
# 컨테이너 타입들(list, set, tuple 등)은 빌트인 타입들을 사용하여
# 대괄호 안에 컨테이너 요소들의 타입 힌트를 작성할 수 있다
# 단 이런 경우에 빌트인 타입들을 사용하는 것은 파이썬 3.9버전 이상에서만 가능하다
x: list[int] = [1]
x: set[int] = {6, 7}
# 파이썬 3.8 이전 버전에서는 위와 같은 타입 힌트를 사용하려면
# typing 모듈에서 대문자로 시작하는 해당 타입을 임포트해서 사용해야한다
x: List[int] = [1]
x: Set[int] = {6, 7}
# 딕셔너리에선 키와 밸류 모두 타입 지정이 필요하다
x: dict[str, float] = {"field": 2.0} # 파이썬 3.9+
x: Dict[str, float] = {"field": 2.0}
# 고정된 크기의 튜플은 모든 요소의 타입을 지정할 수 있다.
x: tuple[int, str, float] = (3, "yes", 7.5) # 파이썬 3.9+
x: Tuple[int, str, float] = (3, "yes", 7.5)
# 고정되지 않은 튜플은 한가지 타입 또는 ellipsis(...)를 사용한다
x: tuple[int, ...] = (1, 2, 3) # 파이썬 3.9+
x: Tuple[int, ...] = (1, 2, 3)
# None이 될 수 있는 값들은 Optional[]을 사용한다
x: Optional[str] = some_function()
# Mypy는 if절에서 None이 될 수 없는 값을 알고 있다.
if x is not None:
print(x.upper())
# 만약 어떤 값이 절대로 None이 될 수 없다면 assert문을 활용하라
assert x is not None
print(x.upper())
Any와 타입힌트가 없는 것의 차이점:
타입 힌트로 Any를 쓰면 임의 타입의 값을 받아들인다고 명시적으로 작성하는 것이며 타입 힌트가 없는 것은 아직 타입 힌트를 받지 않았음을 의미한다
Union
, Optional
, Any
같은 타입은 꼭 필요한 경우에만 사용하는 것이 좋다.
타입 힌트와 어노테이션의 차이
스택 오버플로우 글: 이 글의 댓글 부분에 따르면 타입 힌트(type hinting)은 어노테이션의 한 기능(application)이라고 한다. 타입 힌트보다 어노테이션이 더 큰 개념인 것 같다.
__annotations__
타입 힌트를 적용한 함수나 클래스 같은 객체의 __annotations__
속성에 타입 힌트 메타데이터가 딕셔너리로 저장된다.
# 위의 add 함수의 __annotations__ 출력
print(add.__annotations__)
>>> {'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}
참고자료
클린코드 이제는 파이썬이다
11장 주석과 타입 힌트