νμ νν (Type Hints)
νμ νν (Type Hints)¶
1. νμ νν μ΄λ?¶
νμ νν μ νμ΄μ¬ 3.5+μμ λμ λ κΈ°λ₯μΌλ‘, λ³μλ ν¨μμ νμ μ λͺ μμ μΌλ‘ νμν©λλ€. νμ΄μ¬μ μ¬μ ν λμ νμ μΈμ΄μ΄μ§λ§, νμ ννΈλ₯Ό ν΅ν΄ μ½λμ κ°λ μ±κ³Ό μμ μ±μ λμΌ μ μμ΅λλ€.
# νμ
ννΈ μμ΄
def greet(name):
return f"Hello, {name}"
# νμ
ννΈ μ¬μ©
def greet(name: str) -> str:
return f"Hello, {name}"
νμ νν μ μ₯μ ¶
| μ₯μ | μ€λͺ |
|---|---|
| κ°λ μ± | ν¨μμ μ λ ₯/μΆλ ₯ νμ μ λͺ νν μ μ μμ |
| IDE μ§μ | μλμμ±, νμ μ€λ₯ κ²½κ³ |
| λ¬Έμν | μ½λ μμ²΄κ° λ¬Έμ μν |
| λ²κ·Έ μλ°© | μ μ λΆμμΌλ‘ λ°νμ μ μ€λ₯ λ°κ²¬ |
2. κΈ°λ³Έ νμ ννΈ¶
κΈ°λ³Έ μλ£ν¶
# λ³μ νμ
ννΈ
name: str = "Python"
age: int = 30
price: float = 19.99
is_active: bool = True
data: bytes = b"hello"
# ν¨μ νμ
ννΈ
def add(a: int, b: int) -> int:
return a + b
def say_hello(name: str) -> None:
print(f"Hello, {name}")
컬λ μ νμ ¶
# Python 3.9+ (λ΄μ₯ νμ
μ§μ μ¬μ©)
numbers: list[int] = [1, 2, 3]
names: set[str] = {"Alice", "Bob"}
scores: dict[str, int] = {"math": 95, "english": 88}
point: tuple[int, int] = (10, 20)
coordinates: tuple[float, ...] = (1.0, 2.0, 3.0) # κ°λ³ κΈΈμ΄
# Python 3.8 μ΄ν (typing λͺ¨λ μ¬μ©)
from typing import List, Set, Dict, Tuple
numbers: List[int] = [1, 2, 3]
names: Set[str] = {"Alice", "Bob"}
scores: Dict[str, int] = {"math": 95}
point: Tuple[int, int] = (10, 20)
3. typing λͺ¨λ¶
Optional¶
κ°μ΄ NoneμΌ μ μμμ νμν©λλ€.
from typing import Optional
def find_user(user_id: int) -> Optional[str]:
"""μ¬μ©μλ₯Ό μ°Ύκ±°λ None λ°ν"""
users = {1: "Alice", 2: "Bob"}
return users.get(user_id)
# Python 3.10+ λμ
def find_user(user_id: int) -> str | None:
users = {1: "Alice", 2: "Bob"}
return users.get(user_id)
Union¶
μ¬λ¬ νμ μ€ νλλ₯Ό νμ©ν©λλ€.
from typing import Union
def process(value: Union[int, str]) -> str:
"""int λλ strμ λ°μ λ¬Έμμ΄λ‘ λ³ν"""
return str(value)
# Python 3.10+ λμ
def process(value: int | str) -> str:
return str(value)
Any¶
λͺ¨λ νμ μ νμ©ν©λλ€ (νμ μ²΄ν¬ λΉνμ±ν).
from typing import Any
def log(message: Any) -> None:
print(message)
# μ΄λ€ κ°μ΄λ κ°λ₯
log("hello")
log(123)
log([1, 2, 3])
Callable¶
ν¨μ νμ μ μ§μ ν©λλ€.
from typing import Callable
# Callable[[μΈμνμ
λ€], λ°ννμ
]
def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
return func(a, b)
def add(x: int, y: int) -> int:
return x + y
result = apply(add, 3, 4) # 7
TypeVar¶
μ λ€λ¦ νμ λ³μλ₯Ό μ μν©λλ€.
from typing import TypeVar, List
T = TypeVar('T')
def first(items: List[T]) -> T:
"""리μ€νΈμ 첫 λ²μ§Έ μμ λ°ν"""
return items[0]
# νμ
μ΄ μλμΌλ‘ μΆλ‘ λ¨
num = first([1, 2, 3]) # int
name = first(["a", "b"]) # str
4. κ³ κΈ νμ νν ¶
Generic ν΄λμ€¶
from typing import Generic, TypeVar
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
def is_empty(self) -> bool:
return len(self._items) == 0
# μ¬μ©
int_stack: Stack[int] = Stack()
int_stack.push(1)
int_stack.push(2)
str_stack: Stack[str] = Stack()
str_stack.push("hello")
TypedDict¶
λμ λ리μ ν€μ κ° νμ μ μ μν©λλ€.
from typing import TypedDict
class User(TypedDict):
name: str
age: int
email: str
# μ νν ν€μ νμ
μ΄ νμ
user: User = {
"name": "Alice",
"age": 30,
"email": "alice@example.com"
}
# μ νμ ν€
class UserOptional(TypedDict, total=False):
name: str
age: int
nickname: str # μ νμ
Protocol (ꡬ쑰μ μλΈνμ΄ν)¶
λ νμ΄νμ νμ ννΈλ‘ ννν©λλ€.
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None:
...
class Circle:
def draw(self) -> None:
print("Drawing circle")
class Square:
def draw(self) -> None:
print("Drawing square")
def render(shape: Drawable) -> None:
shape.draw()
# Circle, Squareλ Drawableμ μμνμ§ μμ§λ§
# draw() λ©μλκ° μμΌλ―λ‘ μ¬μ© κ°λ₯
render(Circle()) # OK
render(Square()) # OK
Literal¶
νΉμ 리ν°λ΄ κ°λ§ νμ©ν©λλ€.
from typing import Literal
def set_mode(mode: Literal["read", "write", "append"]) -> None:
print(f"Mode: {mode}")
set_mode("read") # OK
set_mode("write") # OK
# set_mode("delete") # νμ
μλ¬!
Final¶
μμλ₯Ό νμν©λλ€ (μ¬ν λΉ λΆκ°).
from typing import Final
MAX_SIZE: Final = 100
PI: Final[float] = 3.14159
# MAX_SIZE = 200 # νμ
μ²΄μ»€κ° κ²½κ³
5. νμ λ³μΉ¶
볡μ‘ν νμ μ μ΄λ¦μ λΆμ λλ€.
from typing import Dict, List, Tuple
# νμ
λ³μΉ
UserId = int
UserData = Dict[str, str]
UserList = List[Tuple[UserId, UserData]]
def get_users() -> UserList:
return [
(1, {"name": "Alice", "email": "alice@example.com"}),
(2, {"name": "Bob", "email": "bob@example.com"}),
]
# Python 3.10+ TypeAlias
from typing import TypeAlias
Vector: TypeAlias = list[float]
Matrix: TypeAlias = list[Vector]
6. λ°νμ vs μ μ λΆμ¶
νμ ννΈλ λ°νμμ κ°μ λμ§ μμ¶
def greet(name: str) -> str:
return f"Hello, {name}"
# λ°νμμλ λ¬Έμ μμ΄ μ€νλ¨!
result = greet(123) # "Hello, 123"
μ μ νμ 체컀 (mypy)¶
# μ€μΉ
pip install mypy
# νμ
μ²΄ν¬ μ€ν
mypy your_script.py
# example.py
def add(a: int, b: int) -> int:
return a + b
result = add("hello", "world") # mypyκ° μ€λ₯ κ°μ§
$ mypy example.py
example.py:4: error: Argument 1 to "add" has incompatible type "str"; expected "int"
λ°νμ νμ μ²΄ν¬ (μ νμ )¶
from typing import get_type_hints
def validate_types(func):
"""λ°νμ νμ
κ²μ¦ λ°μ½λ μ΄ν°"""
hints = get_type_hints(func)
def wrapper(*args, **kwargs):
# μΈμ νμ
κ²μ¦
for (name, value), expected_type in zip(
kwargs.items(), hints.values()
):
if not isinstance(value, expected_type):
raise TypeError(f"{name} must be {expected_type}")
return func(*args, **kwargs)
return wrapper
7. μ€μ©μ ν¨ν΄¶
API μλ΅ νμ μ μ¶
from typing import TypedDict, List, Optional
class APIResponse(TypedDict):
success: bool
data: Optional[dict]
error: Optional[str]
class User(TypedDict):
id: int
name: str
email: str
class UsersResponse(TypedDict):
users: List[User]
total: int
page: int
def fetch_users(page: int = 1) -> UsersResponse:
# API νΈμΆ λ‘μ§
return {
"users": [{"id": 1, "name": "Alice", "email": "alice@example.com"}],
"total": 100,
"page": page
}
μ€μ ν΄λμ€¶
from typing import Final, ClassVar
class Config:
# ν΄λμ€ λ³μ (λͺ¨λ μΈμ€ν΄μ€ 곡μ )
DEBUG: ClassVar[bool] = False
VERSION: ClassVar[str] = "1.0.0"
# μμ
MAX_CONNECTIONS: Final = 100
TIMEOUT: Final[int] = 30
μ€λ²λ‘λ¶
κ°μ ν¨μμ μ¬λ¬ μκ·Έλμ²λ₯Ό μ μν©λλ€.
from typing import overload, Union
@overload
def process(value: int) -> int: ...
@overload
def process(value: str) -> str: ...
def process(value: Union[int, str]) -> Union[int, str]:
if isinstance(value, int):
return value * 2
return value.upper()
# νμ
μ²΄μ»€κ° μ¬λ°λ₯Έ λ°ν νμ
μ μΆλ‘
num: int = process(5) # int
text: str = process("hi") # str
8. μμ£Ό μ°λ νμ μ 리¶
| νμ | μ€λͺ | μμ |
|---|---|---|
int, str, float, bool |
κΈ°λ³Έ νμ | x: int = 1 |
list[T] |
리μ€νΈ | nums: list[int] |
dict[K, V] |
λμ λ리 | data: dict[str, int] |
set[T] |
μ§ν© | ids: set[int] |
tuple[T, ...] |
νν | point: tuple[int, int] |
Optional[T] |
T λλ None | name: Optional[str] |
Union[T1, T2] |
T1 λλ T2 | value: Union[int, str] |
Callable[[Args], R] |
ν¨μ νμ | fn: Callable[[int], str] |
Any |
λͺ¨λ νμ | data: Any |
TypeVar |
μ λ€λ¦ λ³μ | T = TypeVar('T') |
Protocol |
ꡬ쑰μ νμ΄ν | class Sized(Protocol) |
9. μ°μ΅ λ¬Έμ ¶
μ°μ΅ 1: ν¨μ νμ ννΈ¶
λ€μ ν¨μμ μ μ ν νμ ννΈλ₯Ό μΆκ°νμΈμ.
def calculate_average(numbers):
if not numbers:
return None
return sum(numbers) / len(numbers)
μ°μ΅ 2: μ λ€λ¦ ν¨μ¶
리μ€νΈμμ 쑰건μ λ§μ‘±νλ 첫 λ²μ§Έ μμλ₯Ό μ°Ύλ μ λ€λ¦ ν¨μλ₯Ό μμ±νμΈμ.
# find_first([1, 2, 3, 4], lambda x: x > 2) -> 3
# find_first(["a", "bb", "ccc"], lambda s: len(s) > 1) -> "bb"
μ°μ΅ 3: TypedDict¶
μ¬μ©μ νλ‘νμ λνλ΄λ TypedDictλ₯Ό μ μνμΈμ. - νμ: id (int), username (str), email (str) - μ ν: bio (str), avatar_url (str)
λ€μ λ¨κ³¶
02_Decorators.mdμμ ν¨μμ ν΄λμ€λ₯Ό νμ₯νλ λ°μ½λ μ΄ν°λ₯Ό λ°°μλ΄ μλ€!