파이썬 3.10 타입 힌트 치트시트


이 글은 파이썬 문서를 참고해 파이썬에서의 타입 힌트, 또는 어노테이션 문법을 정리한 글입니다. 이 글은 Python 3.10을 기준으로 작성됐습니다.

변수

변수는 변수명 뒤에 :를 이용해 타입을 명시해줍니다.

x: int = 10
y: str = "abc"
z: bool = True

리스트

x: list = [1, "a", []]
y: list[int] = [1, 2, 3]
z: list[int | str] = [1, 2, "a"]

딕셔너리

x: dict = {}
y: dict[str, int] = {"a": 1, "b": 2, "c": 3}

튜플

x: tuple = (1, "2", 3)
y: tuple[int, str, int] = (1, "2", 3)

유니온 타입

파이썬 3.10부터 Union[int, str]과 같은 타입을 |를 사용해 만들 수 있는 기능이 추가되었습니다.

x: int | str = 1		# a: Union[int, str]과 동일합니다
x = "b"

b: int | None = 1		# b: Optional[int]와 동일합니다
b = None

함수

def add(a: int, b: int) -> int:
    return a + b

Optional parameter의 경우 타입 어노테이션 이후에 기본값을 정해줍니다.

def foo(a: int = 0) -> int:
    return a

Callable

Callable[ArgumentTypes, ReturnType]을 이용해 함수 타이핑이 가능합니다. ...를 이용하면 함수의 ReturnType만을 결정해줄 수 있다.

from collections.abc import Callable

add_function: Callable[[int, int], int] = add
# OR
add_function: Callable[..., int] = add

Generics

Generic Class

제네릭 클래스는 전부 Generic이라는 추상 클래스를 상속합니다. TypeVar을 이용해 제네릭 타입을 사용할 수 있습니다.

from typing import Generic, TypeVar

T = TypeVar("T")

class MyList(Generic[T]):
    
    def __init__(self):
        self.my_list: list[T]

    def append(self, item: T):
        self.my_list.append(item)

여러개의 제네릭 타입을 사용할 수도 있습니닫.

KeyType = TypeVar("KeyType")
ValueType = TypeVar("ValueType")


class MyDict(Generic[KeyType, ValueType]):
    
    def __init__(self):
        self.my_dict: dict[KeyType, ValueType]

    def add(self, key: KeyType, value: ValueType):
        self.my_dict[key] = value

Generic Function

제네릭 클래스와 마찬가지로 TypeVar을 사용해 제네릭 함수를 작성할 수 있습니다.

from typing import Callable, Iterable, TypeVar

T1 = TypeVar("T1")
T2 = TypeVar("T2")

def my_map(func: Callable[[T1], T2], iterables: Iterable[T1]) -> Iterable[T2]:
    return map(func, iterables)

ParamSpec

ParamSpec은 주로 Callable의 파라미터를 다른 Callable로 넘겨주고 싶을 때 사용합니다.

예를 들어 아래 기본 함수에 경과 시간을 출력하는 기능을 추가해주는 함수가 있습니다. 이 함수를 사용해 만든 함수는 파라미터의 타이핑이 되지 않는데 ParamSpec을 쓰면 이 문제를 해결할 수 있습니다.

# 코드 출처: https://www.geeksforgeeks.org/timing-functions-with-decorators-python/
from time import time

def timer_func(func):
    # This function shows the execution time of 
    # the function object passed
    def wrap_func(*args, **kwargs):
        t1 = time()
        result = func(*args, **kwargs)
        t2 = time()
        print(f'Function {func.__name__!r} executed in {(t2-t1):.4f}s')
        return result
    return wrap_func
    
def foo(x: int, y: int) -> int:
    return x + y

timed_foo = timer(foo)
timed_foo("a", "b")     # No warning

ParamSpec을 사용해 타이핑한 경우

from time import time
from typing import Callable, ParamSpec, TypeVar

T = TypeVar("T")
P = ParamSpec("P")


def timer(func: Callable[P, T]) -> Callable[P, T]:
    def wrap_func(*args: P.args, **kwargs: P.kwargs):
        t1 = time()
        result = func(*args, **kwargs)
        t2 = time()
        print(f"Function {func.__name__!r} executed in {(t2-t1):.4f}s")
        return result

    return wrap_func


def foo(x: int, y: int) -> int:
    return x + y


timed_foo = timer(foo)
timed_foo("a", "b")     # Warning