Intro
안녕하세요, 오늘은 python의 type hint에 대해서 알아보겠습니다.
python의 장점은 우리가 정의한 변수들의 타입을 알아서 동적으로 정해주는 것이죠. 하지만 이는 양날의 검이라고 볼 수있습니다. 프로그램 복잡도가 높아질수록, 많은 사람과 협업할수록 변수의 type을 파악해야 사전에 디버그할 수 있고, 적절한 로직을 짤 수 있습니다.
따라서 python에서는 이런 단점을 보완하기 위해서 type hint (a.k.a annotation) 라는 기능을 지원합니다. 이 변수의 type은 무엇인지, 또는 이 함수의 인수는 어떤 type이고, 어떤 type의 결과를 return하는지 명시해주는 것입니다. 이는 강제성은 없지만, mypy와 같은 type checker 또는 pycharm같은 IDE에서 검사를 해서 명시해준 type의 변수, 인수, 리턴 값이 나오지 않으면 경고를 보내줍니다.
하지만 이또한 강건한 프로그래밍이 가능하게 장점과 함께, 개발자의 노력과 시간이 더 소비된다는 단점도 있습니다. 저는 장점이 더 크다고 생각하기 때문에 Real Python 에 정리된 type hint 사용법을 보고 이글에 정리하고자 합니다.
Example1
원기둥 (cylinder)의 부피를 구하는 함수를 구현해보겠습니다. 원기둥의 부피를 구하는 공식은 $\pi r^2h$이고, 반지를 $r$과 높이 $h$만 알면 구할 수 있습니다. type hint를 적용해서 구현해보면 아래와 같습니다.
PI: float = np.pi
def get_cylinder_volume(radius: float, height: float = 1.) -> float:
return PI * np.power(radius, 2) * height
get_cylinder_volume(2)
>> 12.566370614359172
get_cylinder_volume(int(2), 2)
>> 25.132741228718345
get_cylinder_volume.__annotations__
>> {'radius': float, 'height': float, 'return': float}
여기서 볼 point는 4가지 입니다.
- 처음 변수 PI를 선언할 때 type hint를 적용했습니다.
:
을 통해 type을 명시할 수 있습니다. get_cylinder_volume
의 인수 type을 정해주고, return 값의 type은 float이라고 hint를 줬습니다.- type hint와 다른 type의 인수가 전해진다고 해서 에러가 발생하지 않습니다. 하지만 type hint를 지키짐 않으면 나중에 어딘가에서는 에러가 발생할 수도 있겠죠!
- 함수의
__annotations__
를 호출하면, 주어진 hint에 대한 정보가 저장되어있음을 할 수 있습니다. type checker 프로그램들은 이를 참고하겠죠?
추가) 함수가 어떤 값을 반환하지 않는 경우,
-> None
을 써줄수도 있고, 아예 안적어주는 것도 괜찮습니다.
Example2
float, int, str
같은 기본적인 type 쉽게 hint를 줄 수 있지만, list, tupel, dict
와 같은 경우는 추가적인 정보가 필요합니다. 단지 list
로 끝날게 아니라, 어떤 type의 값들로 구성되어 있는지도 알아야 합니다. 따라서 이런 추가적인 정보를 제공하기 위해서는 typing
모듈의 도움을 받아야 합니다. 위 제가 참고한 사이트의 예시를 인용하겠습니다.
from typing import Dict, List, Tuple
names: List[str] = ["Guido", "Jukka", "Ivan"]
version: Tuple[int, int, int] = (3, 7, 1)
options: Dict[str, bool] = {"centered": False, "capitalize": True}
3가지 변수와 type hint로 아래와 같이 정보를 알 수 있습니다.
name
:str
type의 변수로 이루어진list
version
: 3개의 값을 갖고있고, 3개 모두int
type 변수인tuple
, 값의 개수와 hint의 개수가 맞지 않으면 경고가 나갑니다.options
: key값은str
, value값은bool
인dict
추가) 다루고자 하는 data가 list, tuple 둘 다 상관 없을 때는 좀 더 상위 개념인
Sequence
를 사용할 수 있습니다. 하지만 웬만하면 정확하고, 통일성있도록 하는게 좋습니다.
추가) 만약 여러 type의 값을 갖고있는 list의 경우,List[Any]
, 또는List[Union[int, str]]
등으로 표현할 수 있습니다. 좀 더 명확한Union
이 더 좋을 거 같네요.
Example3
typing
모듈에서는 TypeVar
라는 특별한 Type variable이 있습니다. 이는 상황에 따라서 어떤 type의 변수를 받을 수 있는 변수입니다. TypeVar
은 주어진 값들을 모두 아우르면서 가장 specific한 type을 취합니다. 참고한 사이트의 예시를 인용하겠습니다.
아래와 같이 주어진 Sequence
의 값 중 임의로 하나를 선택해주는 choose
라는 함수를 정의했습니다.
import random
from typing import Sequence, TypeVar
Choosable = TypeVar("Choosable")
def choose(items: Sequence[Choosable]) -> Choosable:
return random.choice(items)
주어지는 input에 따라 결정되는 Choosable
의 type은 아래와 같습니다.
# str only
choose(["Guido", "Jukka", "Ivan"])
> Choosable: str
# int only
choose([1, 2, 3])
> Choosable: int
# bool & int & float
choose([True, 42, 3.14])
> Choosable: float
# str & int
choose(["Python", 3, 7])
> Choosable: object
마지막 예시에서는 str
과 int
사이의 공통 parent class가 없기 때문에 최상위 클래스인 object
로 결정되었습니다.
만약 Choosable = TypeVar("Choosable", str, float)
처럼 선언하면, 마지막 예시처럼 정해지는 type이 str
또는 float
이 아닌 경우에는 (int는 float의 하위 클래스라서 괜찮습니다.) 경고가 발생합니다.
마무리
오늘은 python의 type hint에 대해서 알아봤습니다. 오늘 소개한 것 말고도 함수를 인수로 넘겨줬을 때 줄 수 있는 hint인 Callable
, 그리고 기본적으로 None
을 내장하고 있어서 처음에는 None으로 초기화하고 나중에 값을 할당하고 싶을 때 사용할 수 있는 Optional
hint도 있습니다.
글 읽어주셔서 감사합니다!
References
'[Python]' 카테고리의 다른 글
[Python] timeit 모듈과 pandas함수들의 속도 비교 테스트 (0) | 2021.07.18 |
---|---|
[Python] Mixin 이란? (0) | 2021.07.10 |
[Python] Decorator(4) - dataclass, functools (0) | 2021.07.04 |
[Python] Decorator(3) - @classmethod, @staticmethod (0) | 2021.06.28 |
[Python] Decorator (2) - @property (0) | 2021.06.20 |