모던 파이썬 — Python 3.10~3.13 신기능
Python 3.10부터 3.13까지 도입된 주요 신기능들을 실전 예제와 함께 살펴봅니다.
Python 3.10: 구조적 패턴 매칭(match-case)
# 기본 값 매칭
def classify_http_status(status: int) -> str:
match status:
case 200:
return "OK"
case 201:
return "Created"
case 400:
return "Bad Request"
case 401 | 403: # OR 패턴
return "인증/권한 오류"
case code if 500 <= code < 600: # 가드(guard) 조건
return f"서버 오류: {code}"
case _:
return "알 수 없는 상태"
print(classify_http_status(200)) # OK
print(classify_http_status(403)) # 인증/권한 오류
print(classify_http_status(503)) # 서버 오류: 503
# 시퀀스 패턴
def process_command(command: list[str]) -> str:
match command:
case ["quit"]:
return "종료"
case ["go", direction]:
return f"{direction}으로 이동"
case ["go", direction, speed]:
return f"{direction}으로 {speed} 속도 이동"
case ["help", *topics]:
return f"도움말: {', '.join(topics) or '전체'}"
case []:
return "빈 명령"
case _:
return f"알 수 없는 명령: {command}"
print(process_command(["go", "north"])) # 북쪽으로 이동
print(process_command(["go", "east", "fast"])) # 동쪽으로 fast 속도 이동
print(process_command(["help", "move", "look"])) # 도움말: move, look
# 매핑(dict) 패턴
def handle_event(event: dict) -> str:
match event:
case {"type": "click", "x": x, "y": y}:
return f"클릭: ({x}, {y})"
case {"type": "keypress", "key": key, "ctrl": True}:
return f"Ctrl+{key}"
case {"type": "keypress", "key": key}:
return f"키 입력: {key}"
case {"type": t, **rest}:
return f"알 수 없는 이벤트: {t}, 나머지: {rest}"
print(handle_event({"type": "click", "x": 100, "y": 200}))
print(handle_event({"type": "keypress", "key": "S", "ctrl": True}))
# 클래스 패턴
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
@dataclass
class Circle:
center: Point
radius: float
@dataclass
class Rectangle:
top_left: Point
bottom_right: Point
def describe_shape(shape) -> str:
match shape:
case Circle(center=Point(x=0, y=0), radius=r):
return f"원점 중심 원, 반지름={r}"
case Circle(center=c, radius=r):
return f"원: 중심=({c.x},{c.y}), 반지름={r}"
case Rectangle(top_left=Point(x=x1, y=y1), bottom_right=Point(x=x2, y=y2)):
return f"직사각형: ({x1},{y1}) ~ ({x2},{y2})"
print(describe_shape(Circle(Point(0, 0), 5.0)))
print(describe_shape(Circle(Point(3, 4), 2.0)))
print(describe_shape(Rectangle(Point(0, 0), Point(10, 5))))
Python 3.10: X | Y 유니온 문법
# 기존: Union[str, None], Optional[str]
# 신규: str | None (Python 3.10+)
def find_user(user_id: int) -> dict | None:
users = {1: {"name": "Alice"}, 2: {"name": "Bob"}}
return users.get(user_id)
def process_value(value: int | str | list) -> str:
return str(value)
# 런타임에서도 isinstance와 함께 동작
def format_value(v: int | float | str) -> str:
if isinstance(v, int | float): # Python 3.10+: isinstance에서도 | 사용 가능
return f"{v:.2f}" if isinstance(v, float) else str(v)
return v.strip()
print(format_value(42)) # 42
print(format_value(3.14)) # 3.14
print(format_value(" hi ")) # hi
# 타입 별칭
Vector = list[float]
Matrix = list[Vector]
Scalar = int | float
Numeric = int | float | complex
Python 3.11: 예외 그룹과 ExceptionGroup
# Python 3.11+: ExceptionGroup — 여러 예외를 동시에 처리
def process_items(items: list[int]) -> list[int]:
errors = []
results = []
for item in items:
try:
if item < 0:
raise ValueError(f"음수 값: {item}")
if item == 0:
raise ZeroDivisionError("0으로 나누기")
results.append(100 // item)
except (ValueError, ZeroDivisionError) as e:
errors.append(e)
if errors:
raise ExceptionGroup("처리 오류", errors)
return results
try:
process_items([5, -3, 0, 2, -1])
except* ValueError as eg:
print(f"ValueError 발생 {len(eg.exceptions)}건:")
for e in eg.exceptions:
print(f" - {e}")
except* ZeroDivisionError as eg:
print(f"ZeroDivisionError 발생 {len(eg.exceptions)}건")
# Python 3.11+: add_note() — 예외에 메모 추가
try:
x = int("not_a_number")
except ValueError as e:
e.add_note("사용자가 입력한 값이 숫자가 아닙니다.")
e.add_note("올바른 형식: 1, 42, -5 등 정수만 허용")
raise
Python 3.11: Self 타입
from typing import Self
from __future__ import annotations # 이전 Python에서도 동작
class Builder:
def __init__(self) -> None:
self._parts: list[str] = []
def add(self, part: str) -> Self: # Self: 현재 클래스 타입 반환
self._parts.append(part)
return self
def build(self) -> str:
return " ".join(self._parts)
class ExtendedBuilder(Builder):
def add_many(self, *parts: str) -> Self: # Self: ExtendedBuilder 반환
for part in parts:
self.add(part)
return self
# 타입 추론 정확: ExtendedBuilder를 반환
builder = ExtendedBuilder()
result = builder.add("Hello").add_many("World", "!").build()
print(result) # Hello World !
Python 3.12: 타입 매개변수 문법 (PEP 695)
# Python 3.12+: 새로운 제네릭 문법
# 기존: from typing import TypeVar, Generic; T = TypeVar('T')
# 신규: type[T] 문법
# 함수 제네릭
def first[T](items: list[T]) -> T | None:
return items[0] if items else None
# 클래스 제네릭
class Stack[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()
# 타입 별칭 (Python 3.12+)
type Vector = list[float]
type Matrix = list[Vector]
type Callback[T] = (T) -> None
# 제약이 있는 TypeVar
def double[T: (int, float)](x: T) -> T:
return x * 2
print(double(5)) # 10
print(double(3.14)) # 6.28
Python 3.12: @override 데코레이터
from typing import override
class Animal:
def speak(self) -> str:
return "..."
def move(self) -> str:
return "이동"
class Dog(Animal):
@override
def speak(self) -> str: # 타입 체커가 부모에 speak()가 있는지 확인
return "왈왈!"
@override
def moove(self) -> str: # 오타! 타입 체커가 경고
return "달린다!"
Python 3.13: 대화형 인터프리터 개선 & 기타
# Python 3.13: copy.replace() — 불변 객체 수정
import copy
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: float
y: float
p = Point(1.0, 2.0)
p2 = copy.replace(p, x=10.0) # 새 Point(10.0, 2.0) 반환
print(p) # Point(x=1.0, y=2.0)
print(p2) # Point(x=10.0, y=2.0)
# Python 3.11+: tomllib — TOML 파싱 표준 라이브러리
import tomllib
toml_content = b"""
[tool.poetry]
name = "my-project"
version = "0.1.0"
description = "테스트 프로젝트"
[tool.poetry.dependencies]
python = "^3.12"
pydantic = "^2.0"
[database]
host = "localhost"
port = 5432
name = "mydb"
"""
config = tomllib.loads(toml_content.decode())
print(f"프로젝트: {config['tool']['poetry']['name']}")
print(f"DB 호스트: {config['database']['host']}")
# pyproject.toml 파일 읽기 예시
# with open("pyproject.toml", "rb") as f:
# config = tomllib.load(f)
버전별 신기능 정리
# Python 3.10 주요 신기능
# - match-case 구조적 패턴 매칭 (PEP 634)
# - X | Y 유니온 타입 문법 (PEP 604)
# - 더 친절한 오류 메시지 (SyntaxError 등)
# - parenthesized context managers: (with a as x, b as y:)
# Python 3.11 주요 신기능
# - ExceptionGroup + except* (PEP 654)
# - Self 타입 (PEP 673)
# - LiteralString 타입 (PEP 675)
# - Required/NotRequired for TypedDict (PEP 655)
# - 성능 대폭 향상 (평균 25% 빠름, CPython 최적화)
# - tomllib 표준 라이브러리 추가 (PEP 680)
# Python 3.12 주요 신기능
# - 새 제네릭 문법: type aliases, type[T], TypeVarTuple (PEP 695)
# - @override 데코레이터 (PEP 698)
# - f-string 표현식 개선 (따옴표, 여러 줄 허용)
# - 성능 추가 개선
# Python 3.13 주요 신기능
# - 개선된 대화형 인터프리터 (컬러 출력, 멀티라인 편집)
# - copy.replace() (PEP 759)
# - JIT 컴파일러 실험적 지원
# - GIL 제거 실험적 지원 (free-threaded CPython, PEP 703)
고수 팁: f-string 개선 (Python 3.12+)
# Python 3.12+: f-string 안에 같은 따옴표 사용 가능
items = ["apple", "banana", "cherry"]
# 이전 버전: 따옴표 충돌로 불편
# result = f"아이템: {', '.join(items)}"
# Python 3.12+: 같은 따옴표 OK
result = f"아이템: {", ".join(items)}"
print(result)
# f-string 안에서 백슬래시 허용
path = f"경로: {"\n".join(["/usr", "/bin", "/etc"])}"
# 중첩 f-string
value = 42
msg = f"값: {f"{value:08b}"} (이진수)" # 값: 00101010 (이진수)
print(msg)
정리
| 버전 | 주요 기능 | 키워드 |
|---|---|---|
| 3.10 | 구조적 패턴 매칭 | match-case |
| 3.10 | 유니온 타입 단축 | X | Y |
| 3.11 | 다중 예외 처리 | ExceptionGroup, except* |
| 3.11 | 셀프 타입 반환 | Self |
| 3.11 | TOML 파싱 | tomllib |
| 3.12 | 제네릭 문법 간소화 | type[T], type 별칭 |
| 3.12 | 오버라이드 검증 | @override |
| 3.13 | 실험적 JIT/GIL-free | 성능 향상 |
모던 파이썬은 타입 안전성, 표현력, 성능 세 방향으로 꾸준히 발전하고 있습니다.