타입 힌트

bong·2024년 7월 3일

Python

목록 보기
9/9

📢 동적 타입 언어

파이썬은 동적 타입 언어이다. 동적 타입 언어는 런타임 시에 변수에 할당된 값에 따라 타입이 결정된다. 동적 타입 언어는 코드 작성 시 타입을 정의하지 않아도 되기 때문에 코드 작성이 더 간결하고, 편리하다는 장점이 있다.

a = 10
b = 'bbb'

print(type(a), type(b))
>>> <class 'int'> <class 'str'>

위 처럼 변수 선언 시 타입을 정의하지 않아도 할당된 값에 따라 타입이 정해진다.

💡 타입 힌트

타입 힌트란 말 그대로 타입에 대한 힌트를 적어주는 것이다. 변수 옆에 : 기호를 사용하여 나타낸다.

num: int = 100  # 변수 num은 int type
sentence: str = "hello"  # 변수 sentence는 str형 type

# int type 변수 a와 str type 변수 b를 받아 bool type 변수를 리턴하는 함수 func
def func(a: int, b: str) -> bool:
    return True

위 처럼 타입 힌트를 작성할 수 있다. 정적 타입 언어의 변수 선언과 비슷해보이지만, 차이점은 타입 힌트는 힌트일 뿐이라는 것이다.

num: int = "hi"  # 타입 힌트에 int라고 했지만 문자열 저장

def func(a: int, b: str) -> bool:
    return True

func(a="bye", b=123)  # 타입 힌트 무시

타입 힌트는 지키지 않아도 에러가 발생하지 않는다.

❓ 타입 힌트를 사용하는 이유

동적 타입 언어는 타입을 따로 선언해도 되지 않아서 생산성에 강점이 있다. 하지만 언제부턴가 정적 타입 언어처럼(강제는 아니지만) 파이썬에서 타입 힌트를 사용해서 타입을 명시해주는 것이 일반적인 것으로 되었다. 왜냐하면 동적 타입 언어의 단점이 꽤 크기 때문이다.

💥 에러 발생 확률 줄이기

동적 타입 언어의 큰 단점은 실행되기 전까지 타입 관련 에러를 알기 어렵다는 것이다.

def plus_one(x):
    return x + 1

plus_one("123")

위 처럼 코드를 작성하여도 개발자 본인이 눈치채지 못하면 실행하기 전까지 문제를 인식할 수 없다. 길고 여러 모듈로 나뉘어진 코드를 작성/수정하다 보면 저런 경우가 꽤 있다.

타입 힌트를 작성하게 되면 코드 작성 시에 에디터에서 작성한 타입 힌트를 보여주어 개발자가 에러를 발생시킬 확률을 줄일 수 있다.

타입 힌트 ⚔️ 주석

사실 위에서 말한 타입 힌트의 기능은 주석으로도 할 수 있다. 주석으로 입출력에 대한 타입을 적어줄 수도 있고, 에디터에서 주석 내용도 보여준다.

나도 타입 힌트를 사용하기 전에는 주석을 많이 사용했고, docstring을 sphinx로 문서화해서 협업에서 유용하게 사용했었다. 하지만 주석에는 없는 타입 힌트의 강력한 장점은 자동완성이다.

위를 보면 작성한 타입 힌트를 토대로 변수 x에 대해 int 타입에서 사용되는 메서드들의 자동완성을 지원한다. 주석만 적은 코드에서는 x 다음에 점을 찍어도 아무일도 발생하지 않는다. 자동완성은 타입 힌트의 장점들 중 직접 사용하면서 가장 체감되는 장점이다.

🛠️ 타입 힌트 방법

☝️ Simple 타입

def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
    return item_a, item_b, item_c, item_d, item_d, item_e

str, int, float, bool, bytes 등과 같이 단순히 값 하나를 저장하는 데이터 타입들이다.

✌️ Generic 타입

dict, list, set 등과 같이 다른 데이터들을 저장할 수 있는 구조를 가지는 데이터들이다. typing 모듈을 이용해서 내부의 데이터 타입들까지 힌트를 작성할 수 있다.

from typing import List, Tuple, Set, Dict, Optional

# items는 str 데이터들을 원소로 가지는 list
def process_items(items: List[str]):
    for item in items:
        print(item)

# items_t는 차례대로 int, int, str인 tuple
# items_s는 bytes 데이터들이 원소인 set
def process_items(items_t: Tuple[int, int, str], items_s: Set[bytes]):
    return items_t, items_s

# prices는 key가 str이고, value가 float인 dict
def process_items(prices: Dict[str, float]):
    for item_name, item_price in prices.items():
        print(item_name)
        print(item_price)

# name은 str이거나 None일 수 있다
def say_hi(name: Optional[str] = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")

👌 Annotated를 이용한 커스텀 타입

Annotated[데이터 타입, 메타데이터] 같은 형태로 커스텀 타입을 만들 수 있다.

from typing_extensions import Annotated

int_id = Annotated[int, "0보다 큰 정수만 허용"]

def func(user_id: int_id):  # 커스텀 타입으로 타입 힌트 가능
    pass

🤔 Just Hint?

앞에서 본 타입 힌트의 기능들은 주의, 경고, 편의성 등 말 그대로 '힌트'로써의 역할에 제한된다. 하지만 타입 힌트를 이용해 '힌트'의 범주를 넘어 실제 type checking, validation을 할 수도 있다.

🧐 Type Checking (Mypy)

Mypy를 이용하면 타입 힌트를 지키지 않거나 런타임에 타임 에러가 발생할 부분들을 짚어준다.

🏎️ FastAPI

FastAPI에서는 타입 힌트를 이용해서 데이터 validation에 활용한다. (문서 1, 문서 2)

📚 참조

0개의 댓글