Python은 Dynamic language이기 때문에 variable은 여러 type object를 가르킬 수 있다.
- 이는 매우 높은 유연성을 제공해주고, 작은 규모의 소스코드에서는 잘 동작한다. (특히 type에 대해 자유롭다보니 언어의 진입장벽을 낮춰주는 효과도 있다.)
- 하지만, 이는 runtime에서 TypeError가 발생할 확률이 커지기 때문에 대규모의 프로젝트에서는 버그가 많아진다는 단점을 가진다.
때문에, 안정성을 중시하는 software를 개발하는 입장에서는 compile 단계에서 type check를 통해 문제점을 사전에 해결할 수 있는 static language가 보다 선호되는 경우가 많다.
이같은 단점을 보완하기 위해 Python 3.5부터는 Type Annotation 을 제공하기 시작했다.
Type Annotation
Type annotation을 변수에 적용한 단순한 예는 다음과 같음.
var_int: int = 11
var_float: float = 3.14
var_str: str = 'ABCD'
var_list: list = [0, 1, 2, 3]
다음과 같이 parameter의 type에 대해서도 적용가능함.
def product(a: int, b: int) -> int:
return a*b
하지만, annotation이라는 용어에서 알 수 있듯이 어떤 type으로 해당 parameter에 대한 argument를 입력할지를 알려주기는 하지만, static language에서처럼 compile error등으로 강제하진 못한다.
다음과 같이 다른 type으로 parameter의 값을 지정하더라도 python에서 문제없는 지정이라면 아무 문제 없이 돌아간다. (즉, 일종의 권고안에 불과하다.)
def product(a: int, b: int) -> int:
return a*b
ret_v = product(10.0, 2.0)
Typing모듈과 Static analyzer (mypy)
static language처럼 type check를 하고 싶다면, type annotation을 typing
모듈을 사용하여 처리하고, mypy
또는 Pyright
와 같은 thrid-party static analyzer를 이용하면 된다.
typing
모듈은 보다 명시적이고 구체적으로 type을 지정해줄 수 있다.
다음은 int
를 component로 갖는 Vector
를 지정하는 방식임.
from typing import TypeAlias
Vector: TypeAlias = list[int]
def scale(scalar: int, vector: Vector) -> Vector:
return [scalar * num for num in vector]
new_vector = scale(3,[3,2,1])
만약, 다음과 같이 int
가 아닌 float
argument가 주어지고 이를 test.py
로 저장한 후 mypy test.py
로 체크하면 type이 틀렸다고 알려줌.
from typing import TypeAlias
Vector: TypeAlias = list[int]
def scale(scalar: int, vector: Vector) -> Vector:
return [scalar * num for num in vector]
new_vector = scale(1.7,[3,2,1])
mypy
로 체크한 결과는 다음과 같음.
$ mypy test.py
test.py:9: error: Argument 1 to "scale" has incompatible type "float"; expected "int" [arg-type]
Found 1 error in 1 file (checked 1 source file)
python -m mypy --strict test.py
로 수행해도 됨.
위의 예에서는 명시적으로 TypeAlias
를 이용했지만 다음과 같이 사용할 수도 있다.
Vector = list[int]
def scale(scalar: int, vector: Vector) -> Vector:
return [scalar * num for num in vector]
new_vector = scale(1.7,[3,2,1])
list
, set
, dict
등에서 위와 같이 square bracket을 통해 item들의 type을 지정가능하다.
Python 3.9 전에서는
from typing import List, Tuple, Dict, Set
등을 사용해야 했지만,
현재는list, tuple, dict, set
등으로 처리할 수 있음
(즉, 단순한 경우엔 굳이typing
모듈에 필요없음)
typing
모듈이 필요한 경우는
- 하나의 variable 또는 parameter에 여러가지 type을 허용하고자 하는 경우 :
typing.Unio
을 이용. - 하나의 variable 또는 parameter에 호출이 될 수 있는 function이 지정되도록 하는 경우 :
typing.Callable
을 이용. None
이 지정가능한 parameter의 경우 : `typing.Optional- 재할당이 안되는 경우 : typing.Final`
Python 3.10에서는
Union
대신에|
operator만으로도 처리가 가능함.var_real_number : int | float
typeguard를 이용한 type check
typeguard
모듈의 @typechecked
데코레이터를 이용하여 보다 엄격하게 TypeError
를 발생시킬 수 있다. 이경우 유연성은 부족하지만, 디버깅에서 어느 type으로 인한 것인지가 명확해진다.
from typing import Tuple
from typeguard import typechecked
@typechecked
def calculate_area(dimensions: Tuple[int, int]) -> int:
width, height = dimensions
return width * height
my_dimensions = (10, 20)
area = calculate_area(my_dimensions)
print(area)
my_wrong_dimensions = (10, "20")
area = calculate_area(my_wrong_dimensions) # This will raise a TypeError
print(area)
- 위의 code snippet에서
@typechecked
를 제거하면TypeError
없이 동작한다. @typechecked
가 붙을 경우 엄격한 type check가 이루어지고TypeError
가 발생함.
Union
다음의 함수의 parameter r
은 int
또는 float
의 argument가 할당될 수 있음.
from typing import Union
def ds_squared(
r: Union[int,float]
) -> Union[int,float]:
return r**2
Callable
Callable[[int], str]
는 int
type의 single parameter를 가지며, 반환값이 str
이다.
특이한 경우로 Callable[... , str]
인 경우 어떤 형태의 parameter도 가능함을 의미.
Optional
Optional
을 통해 None
인 경우를 허용하면서 None
이 아닌 경우 어떤 타입인지를 square bracket으로 지정할 수 잇음.
def ds_power(
base : int|float,
exp : Optional[float] = None,
) -> int|float:
if exp == None:
return base**2
else:
return base**exp
Final
from typing import Final
PROTOCOL: Final[str] = 'https'
참고자료
https://docs.python.org/3/library/typing.html
https://www.sourcetrail.com/python/python-strong-type/
'Python' 카테고리의 다른 글
[Python] IPython shell 에서 shell cmds 사용하기. (0) | 2023.09.19 |
---|---|
[Python] else : break checker (0) | 2023.09.18 |
[Python] functools.partial (0) | 2023.08.25 |
[Python] instance methods, class methods, and static methods (0) | 2023.08.20 |
[Python] Class로 수행시간 측정 decorator 만들기 (0) | 2023.08.18 |