Communitygithub.com

python-patterns

파이써닉한 관용구, PEP 8 표준, 타입 힌트 및 견고하고 효율적이며 유지보수 가능한 Python 애플리케이션 구축을 위한 모범 사례. Python 코드를 작성하거나 리뷰할 때, 관용적 구조, 타입 지정, 또는 PEP 8이 의심될 때 사용하세요.

python-patterns란 무엇인가요?

이 기술은 견고하고, 효율적이며, 유지보수 가능한 Python 애플리케이션을 구축하기 위한 파이써닉한 패턴과 모범 사례를 제공합니다. 이는 가독성, 명시성, EAFP(Easier to Ask Forgiveness Than Permission)와 같은 핵심 Python 원칙을 다루며, 타입 힌트, 오류 처리, 컨텍스트 관리자, 컴프리헨션, 제너레이터, 데이터 클래스, 데코레이터, 동시성 패턴, 패키지 구성 및 성능 최적화를 위한 구체적인 지침을 포함합니다. 에이전트 사용자는 Python 코드를 작성, 리뷰 또는 리팩터링할 때 이 기술을 활성화하여 코드 품질, 일관성 및 표준 라이브러리 기능의 효과적인 사용을 보장하는 것이 권장됩니다. 이 기술은 모든 주요 코드 편집기 및 AI 코딩 에이전트와 호환되며, 에이전트 추론과 지침 일치를 위한 SKILL.md 문서 구조를 활용합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/python-patterns

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Python 개발 패턴

견고하고 효율적이며 유지보수 가능한 애플리케이션 구축을 위한 파이써닉한 패턴과 모범 사례입니다.

활성화 시기

  • 새 Python 코드 작성 시
  • Python 코드 리뷰 시
  • 기존 Python 코드 리팩터링 시
  • Python 패키지/모듈 설계 시

핵심 원칙

1. 가독성이 중요합니다

Python은 가독성을 우선시합니다. 코드는 명확하고 이해하기 쉬워야 합니다.

# 좋음: 명확하고 읽기 쉬움
def get_active_users(users: list[User]) -> list[User]:
    """제공된 목록에서 활성 사용자만 반환합니다."""
    return [user for user in users if user.is_active]


# 나쁨: 영리하지만 혼란스러움
def get_active_users(u):
    return [x for x in u if x.a]

2. 명시적인 것이 암시적인 것보다 낫습니다

매직을 피하고 코드가 무엇을 하는지 명확히 하세요.

# 좋음: 명시적인 구성
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# 나쁨: 숨겨진 부작용
import some_module
some_module.setup()  # 이게 뭘 하는 거죠?

3. EAFP - 허락보다 용서를 구하는 것이 더 쉽습니다

Python은 조건 확인보다 예외 처리를 선호합니다.

# 좋음: EAFP 스타일
def get_value(dictionary: dict, key: str, default_value: Any = None) -> Any:
    try:
        return dictionary[key]
    except KeyError:
        return default_value

# 나쁨: LBYL(뛰기 전에 살펴보기) 스타일
def get_value(dictionary: dict, key: str, default_value: Any = None) -> Any:
    if key in dictionary:
        return dictionary[key]
    else:
        return default_value

타입 힌트

기본 타입 애너테이션

from typing import Optional, List, Dict, Any

def process_user(
    user_id: str,
    data: Dict[str, Any],
    active: bool = True
) -> Optional[User]:
    """사용자를 처리하고 업데이트된 User 또는 None을 반환합니다."""
    if not active:
        return None
    return User(user_id, data)

최신 타입 힌트 (Python 3.9+)

# Python 3.9+ - 내장 타입 사용
def process_items(items: list[str]) -> dict[str, int]:
    return {item: len(item) for item in items}

# Python 3.8 및 이전 - typing 모듈 사용
from typing import List, Dict

def process_items(items: List[str]) -> Dict[str, int]:
    return {item: len(item) for item in items}

타입 별칭과 TypeVar

from typing import TypeVar, Union

# 복잡한 타입을 위한 타입 별칭
JSON = Union[dict[str, Any], list[Any], str, int, float, bool, None]

def parse_json(data: str) -> JSON:
    return json.loads(data)

# 제네릭 타입
T = TypeVar('T')

def first(items: list[T]) -> T | None:
    """첫 번째 항목을 반환하거나 목록이 비어 있으면 None을 반환합니다."""
    return items[0] if items else None

프로토콜 기반 덕 타이핑

from typing import Protocol

class Renderable(Protocol):
    def render(self) -> str:
        """객체를 문자열로 렌더링합니다."""

def render_all(items: list[Renderable]) -> str:
    """Renderable 프로토콜을 구현하는 모든 항목을 렌더링합니다."""
    return "\n".join(item.render() for item in items)

오류 처리 패턴

특정 예외 처리

# 좋음: 특정 예외 잡기
def load_config(path: str) -> Config:
    try:
        with open(path) as f:
            return Config.from_json(f.read())
    except FileNotFoundError as e:
        raise ConfigError(f"설정 파일을 찾을 수 없음: {path}") from e
    except json.JSONDecodeError as e:
        raise ConfigError(f"설정의 JSON이 잘못됨: {path}") from e

# 나쁨: 맨손 except
def load_config(path: str) -> Config:
    try:
        with open(path) as f:
            return Config.from_json(f.read())
    except:
        return None  # 조용한 실패!

예외 연결

def process_data(data: str) -> Result:
    try:
        parsed = json.loads(data)
    except json.JSONDecodeError as e:
        # 추적을 보존하기 위해 예외 연결
        raise ValueError(f"데이터 구문 분석 실패: {data}") from e

사용자 정의 예외 계층 구조

class AppError(Exception):
    """모든 애플리케이션 오류에 대한 기본 예외입니다."""
    pass

class ValidationError(AppError):
    """입력 유효성 검사가 실패할 때 발생합니다."""
    pass

class NotFoundError(AppError):
    """요청한 리소스를 찾을 수 없을 때 발생합니다."""
    pass

# 사용법
def get_user(user_id: str) -> User:
    user = db.find_user(user_id)
    if not user:
        raise NotFoundError(f"사용자를 찾을 수 없음: {user_id}")
    return user

컨텍스트 관리자

리소스 관리

# 좋음: 컨텍스트 관리자 사용
def process_file(path: str) -> str:
    with open(path, 'r') as f:
        return f.read()

# 나쁨: 수동 리소스 관리
def process_file(path: str) -> str:
    f = open(path, 'r')
    try:
        return f.read()
    finally:
        f.close()

사용자 정의 컨텍스트 관리자

from contextlib import contextmanager

@contextmanager
def timer(name: str):
    """코드 블록의 시간을 측정하는 컨텍스트 관리자입니다."""
    start = time.perf_counter()
    yield
    elapsed = time.perf_counter() - start
    print(f"{name} took {elapsed:.4f} seconds")

# 사용법
with timer("data processing"):
    process_large_dataset()

컨텍스트 관리자 클래스

class DatabaseTransaction:
    def __init__(self, connection):
        self.connection = connection

    def __enter__(self):
        self.connection.begin_transaction()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            self.connection.commit()
        else:
            self.connection.rollback()
        return False  # 예외를 억제하지 않음

# 사용법
with DatabaseTransaction(conn):
    user = conn.create_user(user_data)
    conn.create_profile(user.id, profile_data)

컴프리헨션과 제너레이터

리스트 컴프리헨션

# 좋음: 간단한 변환을 위한 리스트 컴프리헨션
names = [user.name for user in users if user.is_active]

# 나쁨: 수동 루프
names = []
for user in users:
    if user.is_active:
        names.append(user.name)

# 복잡한 컴프리헨션은 확장해야 함
# 나쁨: 너무 복잡함
result = [x * 2 for x in items if x > 0 if x % 2 == 0]

# 좋음: 제너레이터 함수 사용
def filter_and_transform(items: Iterable[int]) -> list[int]:
    result = []
    for x in items:
        if x > 0 and x % 2 == 0:
            result.append(x * 2)
    return result

제너레이터 표현식

# 좋음: 지연 평가를 위한 제너레이터
total = sum(x * x for x in range(1_000_000))

# 나쁨: 큰 중간 리스트를 생성함
total = sum([x * x for x in range(1_000_000)])

제너레이터 함수

def read_large_file(path: str) -> Iterator[str]:
    """큰 파일을 한 줄씩 읽습니다."""
    with open(path) as f:
        for line in f:
            yield line.strip()

# 사용법
for line in read_large_file("huge.txt"):
    process(line)

데이터 클래스와 네임드 튜플

데이터 클래스

from dataclasses import dataclass, field
from datetime import datetime

@dataclass
class User:
    """자동 __init__, __repr__, __eq__가 있는 사용자 엔터티."""
    id: str
    name: str
    email: str
    created_at: datetime = field(default_factory=datetime.now)
    is_active: bool = True

# 사용법
user = User(
    id="123",
    name="Alice",
    email="[email protected]"
)

유효성 검사가 있는 데이터 클래스

@dataclass
class User:
    email: str
    age: int

    def __post_init__(self):
        # 이메일 형식 검증
        if "@" not in self.email:
            raise ValueError(f"잘못된 이메일: {self.email}")
        # 나이 범위 검증
        if self.age < 0 or self.age > 150:
            raise ValueError(f"잘못된 나이: {self.age}")

네임드 튜플

from typing import NamedTuple

class Point(NamedTuple):
    """불변 2D 포인트."""
    x: float
    y: float

    def distance(self, other: 'Point') -> float:
        return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5

# 사용법
p1 = Point(0, 0)
p2 = Point(3, 4)
print(p1.distance(p2))  # 5.0

데코레이터

함수 데코레이터

import functools
import time

def timer(func: Callable) -> Callable:
    """함수 실행 시간을 측정하는 데코레이터."""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)

# slow_function() prints: slow_function took 1.0012s

매개변수화된 데코레이터

def repeat(times: int):
    """함수를 여러 번 반복하는 데코레이터."""
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(times):
                results.append(func(*args, **kwargs))
            return results
        return wrapper
    return decorator

@repeat(times=3)
def greet(name: str) -> str:
    return f"Hello, {name}!"

# greet("Alice")는 ["Hello, Alice!", "Hello, Alice!", "Hello, Alice!"]를 반환합니다.

클래스 기반 데코레이터

class CountCalls:
    """함수가 호출된 횟수를 세는 데코레이터."""
    def __init__(self, func: Callable):
        functools.update_wrapper(self, func)
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"{self.func.__name__}가 {self.count}번 호출되었습니다")
        return self.func(*args, **kwargs)

@CountCalls
def process():
    pass

# process()를 호출할 때마다 호출 횟수가 출력됩니다.

동시성 패턴

I/O 바운드 작업을 위한 스레딩

import concurrent.futures
import threading

def fetch_url(url: str) -> str:
    """URL을 가져옵니다 (I/O 바운드 작업)."""
    import urllib.request
    with urllib.request.urlopen(url) as response:
        return response.read().decode()

def fetch_all_urls(urls: list[str]) -> dict[str, str]:
    """여러 URL을 스레드를 사용하여 동시에 가져옵니다."""
    with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
        future_to_url = {executor.submit(fetch_url, url): url for url in urls}
        results = {}
        for future in concurrent.futures.as_completed(future_to_url):
            url = future_to_url[future]
            try:
                results[url] = future.result()
            except Exception as e:
                results[url] = f"오류: {e}"
    return results

CPU 바운드 작업을 위한 멀티프로세싱

def process_data(data: list[int]) -> int:
    """CPU 집약적인 계산."""
    return sum(x ** 2 for x in data)

def process_all(datasets: list[list[int]]) -> list[int]:
    """여러 데이터셋을 여러 프로세스를 사용하여 처리합니다."""
    with concurrent.futures.ProcessPoolExecutor() as executor:
        results = list(executor.map(process_data, datasets))
    return results

동시 I/O를 위한 Async/Await

import asyncio

async def fetch_async(url: str) -> str:
    """URL을 비동기식으로 가져옵니다."""
    import aiohttp
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

async def fetch_all(urls: list[str]) -> dict[str, str]:
    """여러 URL을 동시에 가져옵니다."""
    tasks = [fetch_async(url) for url in urls]
    results = await asyncio.gather(*tasks, return_exceptions=True)
    return dict(zip(urls, results))

패키지 구성

표준 프로젝트 레이아웃

myproject/
├── src/
│   └── mypackage/
│       ├── __init__.py
│       ├── main.py
│       ├── api/
│       │   ├── __init__.py
│       │   └── routes.py
│       ├── models/
│       │   ├── __init__.py
│       │   └── user.py
│       └── utils/
│           ├── __init__.py
│           └── helpers.py
├── tests/
│   ├── __init__.py
│   ├── conftest.py
│   ├── test_api.py
│   └── test_models.py
├── pyproject.toml
├── README.md
└── .gitignore

임포트 규칙

# 좋음: 임포트 순서 - 표준 라이브러리, 서드파티, 로컬
import os
import sys
from pathlib import Path

import requests
from fastapi import FastAPI

from mypackage.models import User
from mypackage.utils import format_name

# 좋음: 자동 임포트 정렬을 위해 isort 사용
# pip install isort

패키지 내보내기를 위한 init.py

# mypackage/__init__.py
"""mypackage - 샘플 Python 패키지."""

__version__ = "1.0.0"

# 패키지 수준에서 주요 클래스/함수 내보내기
from mypackage.models import User, Post
from mypackage.utils import format_name

__all__ = ["User", "Post", "format_name"]

메모리 및 성능

메모리 효율을 위한 slots 사용

# 나쁨: 일반 클래스는 __dict__를 사용함 (더 많은 메모리)
class Point:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

# 좋음: __slots__는 메모리 사용을 줄임
class Point:
    __slots__ = ['x', 'y']

    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

대용량 데이터를 위한 제너레이터

# 나쁨: 메모리에 전체 목록을 반환함
def read_lines(path: str) -> list[str]:
    with open(path) as f:
        return [line.strip() for line in f]

# 좋음: 한 번에 한 줄씩 생성함
def read_lines(path: str) -> Iterator[str]:
    with open(path) as f:
        for line in f:
            yield line.strip()

루프에서 문자열 연결 피하기

# 나쁨: 문자열 불변성으로 인해 O(n²)
result = ""
for item in items:
    result += str(item)

# 좋음: join을 사용하여 O(n)
result = "".join(str(item) for item in items)

# 좋음: 구축을 위해 StringIO 사용
from io import StringIO

buffer = StringIO()
for item in items:
    buffer.write(str(item))
result = buffer.getvalue()

Python 도구 통합

필수 명령어

# 코드 포맷팅
black .
isort .

# 린팅
ruff check .

# 타입 검사
mypy .

# 테스트
pytest

# 의존성 관리 (pip 사용)
pip freeze > requirements.txt

# 의존성 관리 (Poetry 사용)
poetry export -f requirements.txt --output requirements.txt

추가 자료

  • PEP 8 – Python 코드 스타일 가이드
  • PEP 484 – 타입 힌트
  • PEP 557 – 데이터 클래스
  • Python 모범 사례를 위한 "Fluent Python"

참여하기

이 패턴 세트를 개선하기 위한 제안이나 풀 리퀘스트를 환영합니다. 저장소를 포크하고 개선 사항을 기여해 주세요.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

관련 스킬