C나 Java와 달리, Python은 type을 지정 안해줘도 잘 굴러가는 언어다. 쉽고 간편하게 코드를 작성할 수 있는 장점도 있지만, 프로젝트 크기가 커질수록 관리가 힘들어진다. Javascript 진영에서 Typescript가 대세가 된 것 처럼, Python에서도 type을 지정하는 요구가 커지고있다.
(본 글은 python 3.8 버전을 기준으로 작성했습니다.)
mypy를 이용하면 hint에 맞는 제대로 된 변수가 들어갔는지 체크를 해준다.
$ pip install mypy
$ conda install -c conda-forge mypy
mypy를 설치해주고, 아래와 같이 파일을 실행하면 된다.
$ python main.py <- 이거 대신
$ mypy main.py <- 이렇게 하면 된다.
그럼 이렇게 오류가 있는지 없는지 찾아준다!
이제 본격적으로 Python에서 Type 지정을 어떻게 하는지 알아보자.
var1 = 1
var1: int = 1
var2 = "정준환"
var2: str = "정준환"
def foo(x):
return x
def foo(x: str) -> str:
return x
너무 간단해서 보기만 해도 이해할 수 있다. 조금 더 진화해보자. 이런 진화를 위해서는 typing 이라는 친구의 도움이 필요하다. Python을 설치할 때 같이 설치되므로 따로 설치할 필요는 없다.
def plus(x, y):
return x + y
from typing import Union
def plus(x: Union[int, float], y: Union[int, float]) -> Union[int, float]:
return x + y
더 읽어보기 (안 읽어도 된다는 뜻)
from numbers import Number, Real
def any_func(n: Number)
def any_func(n: Real)
from typing import Union, Optional
def foo_1(x: Union[str, None]) -> str:
return "정준환"
def foo_2(x: Optional[str]) -> str:
return "정준환"
from typing import Optional
Optional[int, float]
그럼 어떻게 해요?
from typing import Union, Optional
Optional[Union[int, float]]
def foo_3(x: int | float) # 3.10 이상
아래는 list에 있는 숫자를 다 더해주는 함수다.
def plus(a_list: list) -> int:
summ = 0
for n in a_list:
summ += n
return summ
str_list = ["안녕", 1, 2, 3]
plus(a_list=str_list)
from typing import List
def plus(a_list: List[int]) -> int:
summ = 0
for n in a_list:
summ += n
return summ
from typing import List, Union
def plus(a_list: List[int | float]) -> int | float: # 3.10 이상
def plus(a_list: List[Union[int, float]]) -> Union[int, float]: # 이전
tuple은 list와 비슷하긴 한데, 한번 만들어지면 못바꿔서 list와는 상황이 조금 다르다. 모든 요소에 대한 type을 다 적어줘야한다.
from typing import Tuple
x: Tuple[int, float, str] = (1, 1.2, "정준환")
y: Tuple[int, int, int] = (1, 2, 3)
이렇게 쓴다. 근데 좀 귀찮지 않나?
from typing import Tuple
a: Tuple[int, ...] = (1, 2, 3, 4) # O
x: Tuple[int, int, ...] = (1, 2, 3, 4) # X
y: Tuple[..., int] = (1, 2, 3, 4) # X
z: Tuple[int, ..., int] = (1, 2, 3, 4) # X
이정도 왔으면 대충 설명해도 잘 알아들을 수 있을 것 같다.
from typing import Dict, Set
x: Dict[str, float] = {"field": 2.0}
x: Set[int] = {6, 7}
def add(x: float, y: float) -> float:
return x + y
def mul(x: float, y: float) -> float:
return x * y
위와 같은 간단한 두가지 함수가 있다고 하자.
def func_n_times(func, x: float, y: float, n: int) -> float:
"""
주어진 function을 n번 적용해서 더하는 함수
"""
return_value = 0
for i in range(n):
return_value += func(x, y)
return return_value
억지로 예시를 하나 만들어봤다. 이런 경우에 func에는 어떤 방식으로 hint를 줄 수 있을까?
아래와 같은 방식을 이용한다. 별로 어렵지 않다.
from typing import Callable
def func_n_times(func: Callable[[float, float], float], x: float,,,)
# Callable[[함수의 input type], return type]
Tuple의 경우랑 비슷하게 return 만 보고싶으면 ...을 활용할 수 있다. 아래처럼 하면 된다.
from typing import Callable
Callable[..., float]
from typing import List
def plus(a_list: List[int]) -> int:
summ = 0
for n in a_list:
summ += n
return summ
위에서 썼던 plus 함수를 다시 가져왔다.
from typing import Iterable
def plus(a_list: Iterable[int]) -> int:
summ = 0
for n in a_list:
summ += n
return summ
이렇게 Iterable 객체는 Iterable 이라고 따로 지정해줄수도 있다!
더 알아보기
아래 예시코드를 살펴보자.
from typing import Union
def any_f(x: Union[str, int]) -> Union[str, int]:
return x
str, int 중 아무거나 들어오면 str, int 중 아무거나 내뱉는다. 어라? 조금 이상한데?
보통 우리는 str이 들어오면 str을, int가 들어오면 int가 return되기를 희망한다. 앞서 배운 Union으로는 이 한계를 해결할 수 없다. 이것을 해결한 것이 바로 TypeVar다. 아래 코드를 살펴보자.
from typing import TypeVar
T = TypeVar("T")
def any_f(x: T) -> T:
return x
from typing import TypeVar, Union
T = TypeVar("T", bound=Union[str, int])
A = TypeVar("A")
B = TypeVar("B")
T_abc = TypeVar("T_abc")
이정도 하면 Python에서 type hint를 주는데 큰 문제가 없을 것 같다. 깔끔하게 코드를 짜보자!
https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html#
https://peps.python.org/pep-0484/#the-numeric-tower
https://stackoverflow.com/questions/72157296/python-iterable-vs-sequence