Quick Reference
from pathlib import Path
def read_names(path: Path) -> list[str]:
return path.read_text(encoding="utf-8").splitlines()
def find_user(user_id: int) -> dict[str, str] | None:
...type hint는 runtime에서 값을 자동 검증하지 않지만 함수 입력·반환·container 모양을 호출자와 type checker에 드러냅니다. list[str], str | None, Mapping, Protocol처럼 필요한 계약만 적고, 외부 입력은 hint와 별도로 runtime validation을 둡니다.
문법
기본형은 함수 주석, 변수 주석, 컬렉션 제네릭, union, Callable과 TypeVar, Protocol까지 같이 보면 됩니다.
from collections.abc import Callable
from typing import Protocol, TypeVar
def greet(name: str) -> str:
...
items: list[str]
value: int | None
handler: Callable[[int], str]
T = TypeVar("T")
class Closeable(Protocol):
def close(self) -> None: ...함수, 변수, 컬렉션, 선택적 값, 함수 타입, 구조적 계약이 각각 다른 층위의 정보를 준다는 점을 같이 보면 타입 힌트가 훨씬 덜 막연해집니다.
기본 계약
매개변수와 반환값을 먼저 적으면 함수 계약이 선명해진다
가장 기본적인 쓰임은 "이 함수가 무엇을 받아 무엇을 돌려주는가"를 보여 주는 것입니다.
def greet(name: str) -> str:
return f"Hello, {name}"이 정도만 있어도 함수 계약이 훨씬 빨리 읽힙니다.
데이터 모양
컬렉션과 union 표기를 읽을 줄 알면 정보량이 크게 늘어난다
현대 Python에서는 list[str], dict[str, int], set[int]처럼 내장 제네릭 표기가 기본입니다.
def summarize(scores: list[int]) -> dict[str, float]:
return {"avg": sum(scores) / len(scores)}컬렉션 안에 무엇이 들어가는지까지 함께 보여 주므로, 단순 list보다 훨씬 정보량이 큽니다.
names: list[str] = ["Mina", "Jin"]
scores: dict[str, int] = {"mina": 95}
tags: set[str] = {"python", "async"}값이 없을 수 있으면 T | None, 여러 타입이 가능하면 A | B처럼 씁니다.
def find_user(user_id: int) -> dict[str, str] | None:
...
value: int | str중요한 건 타입 힌트를 쓰는 것 자체보다, 호출자가 어떤 분기 처리를 해야 하는지를 드러내는 데 있습니다.
관계 표현
함수 타입, 제네릭 관계, 구조적 계약은 typing 도구를 쓴다
조금 더 전문적인 코드에서는 typing 모듈의 도구가 필요합니다.
from collections.abc import Callable
from typing import Protocol, TypeVar
def apply(fn: Callable[[int], str], value: int) -> str:
return fn(value)
T = TypeVar("T")
def first(items: list[T]) -> T:
return items[0]
class Closeable(Protocol):
def close(self) -> None: ...Callable: 함수를 인수로 받을 때TypeVar: 입력과 출력 타입 관계를 연결할 때Protocol: 특정 메서드 시그니처를 만족하는 타입을 표현할 때
런타임 경계
타입 힌트와 런타임 검증은 별개다
타입 힌트를 달았다고 런타임에서 자동 검사가 생기지는 않습니다.
실제 값 검증이 필요하면 isinstance나 직접 검사 코드를 여전히 써야 합니다.
def greet(name: str) -> str:
return f"Hello, {name}"
result = greet(123) # type checker는 경고 가능, 런타임은 자동 차단 안 함주의할 점
타입 힌트를 달았다고 런타임 검사가 자동으로 생기지는 않습니다. 힌트와 실제 값 검증은 별개입니다.
# ❌ 타입 힌트가 자동으로 막아 줄 거라고 기대
def greet(name: str) -> str:
return f"Hello, {name}"
print(greet(123))
# ✅ 타입 힌트는 계약 설명이고, 필요하면 런타임 검증을 직접 추가
def greet(name: str) -> str:
if not isinstance(name, str):
raise TypeError("name must be str")
return f"Hello, {name}"# ❌ 너무 넓은 Any를 쓰면 계약 정보가 사실상 사라진다
from typing import Any
def render(value: Any) -> Any:
return value
# ✅ 정말 여러 타입이면 union이나 Protocol로 더 구체화
def render(value: str | int) -> str:
return str(value)참고 링크
2 sources