본문 바로가기
Python

[Python] Type Annotation : 파이썬에서 변수 및 함수에 type 지정.

by ds31x 2023. 8. 30.

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 rint 또는 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

 

typing — Support for type hints

Source code: Lib/typing.py This module provides runtime support for type hints. For the original specification of the typing system, see PEP 484. For a simplified introduction to type hints, see PE...

docs.python.org

https://www.sourcetrail.com/python/python-strong-type/

 

Solved: strong type in Python - SourceTrail

Are you looking to improve your website's typography? If so, you're in luck! Strong type is a powerful tool that can help you improve your website's readability and overall user experience. In this article, we'll explore the basics of strong type and how y

www.sourcetrail.com