← 파이썬 강의 목록으로
🚀
고급 (Advanced)
정적 타입 · typing · dataclass · frozen

1주차 — 타입 힌트와 dataclass

타입 힌트 문법, typing 모듈(Optional/Union/Callable), 그리고 보일러플레이트를 줄여주는 @dataclass 와 frozen 옵션까지 — 견고한 코드의 출발점입니다.

typingdataclassmypyfield
소요 시간
2시간
난이도
📊 고급
선수 조건
🎯 중급 7~8주차 (OOP)
결과물
정적 타입과 dataclass로 견고한 모델 작성

이 강의에서 배우는 것

  • 1타입 힌트 문법(int, str, list[int], Optional)을 익힌다
  • 2mypy 로 정적 타입을 검사한다
  • 3@dataclass 로 데이터 클래스를 간결히 작성한다
  • 4field(default_factory=list) 의 의의를 안다

1. 타입 힌트 기본

런타임에는 영향이 없지만, IDE 자동완성·정적 분석·문서화에 큰 도움이 됩니다.

python
def greet(name: str, age: int = 20) -> str:
    return f"{name}({age})"

count: int = 0
items: list[str] = []

2. typing 모듈

python
from typing import Optional, Union, Any, Callable

def find_user(uid: int) -> Optional[dict]:
    """Optional[X] = X | None"""
    ...

def parse(value: Union[str, int]) -> int:
    """str 또는 int (Python 3.10+ 에서는 str | int)"""
    ...

handler: Callable[[int], str] = lambda x: str(x)

Python 3.10+ 부터는 더 짧게 쓸 수 있습니다.

python
def find_user(uid: int) -> dict | None: ...
def parse(value: str | int) -> int: ...

3. 컨테이너 타입

python
nums: list[int] = [1, 2, 3]
scores: dict[str, int] = {"Alice": 90}
pair: tuple[int, str] = (1, "a")
unique: set[int] = {1, 2, 3}

4. mypy

bash
pip install mypy
mypy main.py
mypy --strict main.py    # 엄격 모드

5. dataclass

__init__, __repr__, __eq__ 가 자동 생성됩니다.

python
from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

p = Point(1, 2)
print(p)              # Point(x=1, y=2)
print(p == Point(1, 2))   # True

6. dataclass 옵션

python
from dataclasses import dataclass, field

@dataclass
class Book:
    title: str
    author: str
    tags: list[str] = field(default_factory=list)
    price: float = 0.0

b = Book("Python", "Guido")
b.tags.append("language")
⚠️

tags: list[str] = [] 는 모든 인스턴스가 공유합니다. 절대 안 됨 — 반드시 field(default_factory=list).

7. frozen dataclass

불변 객체. 해시 가능 → set/dict 키로 사용 가능합니다.

python
@dataclass(frozen=True)
class Point:
    x: int
    y: int

p = Point(1, 2)
# p.x = 99   # FrozenInstanceError
{Point(1, 2)}   # OK

자주 하는 실수

  1. 가변 기본값을 그대로 — field(default_factory=list) 사용
  2. Optional 의미 오해 — Optional[int] = int | None. '선택적 인자'가 아님
  3. 타입 힌트가 런타임에 검증된다고 오해 — 그냥 힌트일 뿐. 런타임 검증은 pydantic

FAQ

Q1. 타입 힌트를 다 달아야 하나요? — 공개 API(함수 시그니처)는 강력 권장. 내부 변수는 추론으로 충분.

Q2. dataclass vs pydantic.BaseModel — dataclass는 표준, pydantic은 런타임 검증·직렬화 강력. 데이터 처리 많으면 pydantic.

Q3. Any 를 쓰면? — mypy가 검사를 포기. 가능하면 더 구체적인 타입.

💻 예제 (examples)

실제로 실행해 결과를 확인할 수 있는 예제 코드입니다.

01_type_hints.py기본 타입 힌트
CODE
def greet(name: str, age: int = 20) -> str:
    return f"{name}({age})"

count: int = 0
items: list[str] = ["사과", "바나나"]

print(greet("홍길동"))
print(greet("김파이", 28))
print(count, items)
▶ 실행 결과
홍길동(20)
김파이(28)
0 ['사과', '바나나']
02_typing_module.pyOptional/Union/Callable
CODE
from typing import Optional, Callable

def find_user(uid: int) -> Optional[dict]:
    return {"id": 1, "name": "Alice"} if uid == 1 else None

def apply(f: Callable[[int], int], x: int) -> int:
    return f(x)

print(find_user(1))
print(find_user(99))
print(apply(lambda n: n * n, 5))
▶ 실행 결과
{'id': 1, 'name': 'Alice'}
None
25
03_dataclass.pydataclass 기본
CODE
from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1)
print(p1 == p2)
▶ 실행 결과
Point(x=1, y=2)
True
04_dataclass_advanced.pyfield, frozen
CODE
from dataclasses import dataclass, field

@dataclass
class Book:
    title: str
    tags: list[str] = field(default_factory=list)

@dataclass(frozen=True)
class Coord:
    x: int
    y: int

b = Book("Python")
b.tags.append("language")
print(b)
print({Coord(1, 2), Coord(1, 2), Coord(3, 4)})
▶ 실행 결과
Book(title='Python', tags=['language'])
{Coord(x=1, y=2), Coord(x=3, y=4)}

📝 과제 (exercises)

직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.

과제 1

함수에 타입 힌트

목표: 기본 함수 시그니처에 타입 힌트를 추가한다.

요구사항
  • add(a: int, b: int) -> int 타입 힌트 부여
  • join(words: list[str], sep: str = ', ') -> str 타입 힌트 부여
💡 힌트

기본값이 있어도 타입 힌트는 콜론 뒤에 그대로 쓴다

입출력 예시
5
사과, 바나나, 포도
채점
  • · mypy main.py 가 오류 없이 통과
  • · 함수가 정상 동작
정답 코드 펼치기 / 접기
SOLUTION
def add(a: int, b: int) -> int:
    return a + b

def join(words: list[str], sep: str = ", ") -> str:
    return sep.join(words)

print(add(2, 3))
print(join(["사과", "바나나", "포도"]))
▶ 실행 결과
5
사과, 바나나, 포도
과제 2

Book/Member dataclass

목표: 도서관 도메인을 dataclass 로 모델링한다.

요구사항
  • @dataclass Book(title, author, tags: list[str] = field(default_factory=list))
  • @dataclass Member(name, age: int)
  • 두 인스턴스를 만들어 출력
💡 힌트

가변 기본값은 field(default_factory=list)

입출력 예시
Book(title='파이썬', author='홍길동', tags=['언어'])
Member(name='Alice', age=25)
채점
  • · dataclass 데코레이터 사용
  • · field 사용 정확
  • · 출력 일치
정답 코드 펼치기 / 접기
SOLUTION
from dataclasses import dataclass, field

@dataclass
class Book:
    title: str
    author: str
    tags: list[str] = field(default_factory=list)

@dataclass
class Member:
    name: str
    age: int

b = Book("파이썬", "홍길동")
b.tags.append("언어")
m = Member("Alice", 25)
print(b)
print(m)
▶ 실행 결과
Book(title='파이썬', author='홍길동', tags=['언어'])
Member(name='Alice', age=25)
과제 3

Optional 시그니처

목표: Optional 을 사용하는 함수 5개를 작성한다.

요구사항
  • find_user(uid: int) -> Optional[dict]
  • first(items: list[int]) -> Optional[int]
  • div(a: int, b: int) -> Optional[float]
  • get_age(name: str) -> Optional[int]
  • parse_int(s: str) -> Optional[int]
💡 힌트

반환할 값이 없으면 None 반환

입출력 예시
{'id': 1}
1
None
None
42
채점
  • · 5개 모두 Optional 반환 타입
  • · None 반환 케이스 처리
정답 코드 펼치기 / 접기
SOLUTION
from typing import Optional

DB = {1: {"id": 1}, 2: {"id": 2}}
AGES = {"Alice": 25}

def find_user(uid: int) -> Optional[dict]:
    return DB.get(uid)

def first(items: list[int]) -> Optional[int]:
    return items[0] if items else None

def div(a: int, b: int) -> Optional[float]:
    return None if b == 0 else a / b

def get_age(name: str) -> Optional[int]:
    return AGES.get(name)

def parse_int(s: str) -> Optional[int]:
    try:
        return int(s)
    except ValueError:
        return None

print(find_user(1))
print(first([1, 2, 3]))
print(div(10, 0))
print(get_age("Bob"))
print(parse_int("42"))
▶ 실행 결과
{'id': 1}
1
None
None
42
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗