Communitygithub.com

python-patterns

파이썬 관용구, PEP 8 표준, 타입 힌트 및 견고하고 효율적이며 유지보수 가능한 파이썬 애플리케이션 구축을 위한 모범 사례입니다. 파이썬 코드를 작성하거나 검토할 때, 관용적 구조, 타이핑, 또는 PEP 8 관련 질문이 있을 때 사용하세요.

python-patterns란 무엇인가요?

이 스킬은 견고하고, 효율적이며, 유지보수 가능한 파이썬 애플리케이션을 구축하기 위한 관용적인 파이썬 패턴과 모범 사례를 포괄적으로 제공합니다. 주요 주제로는 가독성, 명시성(Explicit is Better Than Implicit), EAFP(Easier to Ask Forgiveness Than Permission)와 같은 핵심 원칙, 타입 힌트(기본 주석, 최신 타입 힌트, TypeVar, Protocol 기반 덕 타이핑), 구체적인 예외 처리 및 예외 체인을 포함한 에러 처리 패턴, 컨텍스트 관리자(자원 관리 및 사용자 정의), 리스트 컴프리헨션 및 제너레이터, 데이터 클래스 및 네임드 튜플, 데코레이터(함수형, 매개변수화, 클래스 기반), 동시성 패턴(스레딩, 멀티프로세싱, async/await), 패키지 구성, 메모리 및 성능 최적화 기법, 그리고 파이썬 도구(black, isort, ruff, pytest 등)와의 통합 방법을 다룹니다. 이 스킬은 클로드 코드, 커서, 코덱스와 같은 AI 코딩 에이전트와 함께 사용하도록 설계되어, 새로운 코드 작성, 기존 코드 리뷰, 리팩토링, 또는 모듈/패키지 설계 시 안내 역할을 합니다.

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

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

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

문서

파이썬 개발 패턴

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

활성화 시점

  • 새로운 파이썬 코드를 작성할 때
  • 파이썬 코드를 검토할 때
  • 기존 파이썬 코드를 리팩토링할 때
  • 파이썬 패키지/모듈을 설계할 때

핵심 원칙

1. 가독성은 중요합니다

파이썬은 가독성을 최우선으로 합니다. 코드는 명확하고 이해하기 쉬워야 합니다.

# 좋음: 명확하고 읽기 쉬움
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 - 용서를 구하는 것이 허락을 받는 것보다 쉽습니다

파이썬은 조건을 확인하는 것보다 예외 처리를 선호합니다.

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

# 나쁨: LBYL (Look Before You Leap) 스타일
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

Protocol 기반 덕 타이핑

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} 실행 시간: {elapsed:.4f}초")

# 사용법
with timer("데이터 처리"):
    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__} 실행 시간: {elapsed:.4f}초")
        return result
    return wrapper

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

# slow_function() 출력: slow_function 실행 시간: 1.0012초

매개변수화된 데코레이터

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"안녕하세요, {name}!"

# greet("Alice")는 ["안녕하세요, Alice!", "안녕하세요, Alice!", "안녕하세요, 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 - 샘플 파이썬 패키지."""

__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()

파이썬 도구 통합

필수 명령어

# 코드 포매팅
black .
isort .

# 린팅
ruff check .

# 타입 검사
mypy .

# 테스트
pytest -v --cov=src/

pyproject.toml 예시

[build-system]
requires = ["setuptools>=64.0", "wheel"]
build-backend = "setuptools.backends._legacy:_Backend"

[project]
name = "mypackage"
version = "1.0.0"
description = "샘플 파이썬 패키지"
readme = "README.md"
requires-python = ">=3.9"
dependencies = [
    "requests>=2.28.0",
    "fastapi>=0.100.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.4.0",
    "pytest-cov>=4.1.0",
    "black>=23.7.0",
    "isort>=5.12.0",
    "ruff>=0.1.0",
    "mypy>=1.5.0",
]

[tool.black]
line-length = 88
target-version = ['py39']

[tool.isort]
profile = "black"

[tool.ruff]
line-length = 88

[tool.pytest.ini_options]
minversion = "7.0"
testpaths = ["tests"]

테스팅 패턴

Pytest Fixtures

import pytest
from mypackage.models import User

@pytest.fixture
def sample_user() -> User:
    """테스트를 위한 샘플 사용자를 생성합니다."""
    return User(
        id="123",
        name="Alice",
        email="[email protected]",
        is_active=True
    )

def test_user_creation(sample_user: User):
    """사용자 생성이 올바르게 작동하는지 테스트합니다."""
    assert sample_user.id == "123"
    assert sample_user.email == "[email protected]"

파라미터화된 테스트

@pytest.mark.parametrize("input_value,expected", [
    ("  hello  ", "hello"),
    ("HELLO", "hello"),
    ("  HELLO  ", "hello"),
])
def test_clean_string(input_value: str, expected: str):
    """문자열 정리 함수를 테스트합니다."""
    assert clean_string(input_value) == expected

모의(Mocking)

from unittest.mock import Mock, patch

def test_fetch_user(mocker):
    """pytest-mock을 사용하여 외부 API 호출을 모의합니다."""
    mock_response = {"id": "123", "name": "Alice"}
    mocker.patch('requests.get', return_value=Mock(
        json=lambda: mock_response,
        raise_for_status=lambda: None
    ))

    user = fetch_user("123")
    assert user.name == "Alice"

버전 관리 및 의존성

의존성 관리 모범 사례

# 가상 환경 생성
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# 의존성 설치
pip install -e ".[dev]"

# 의존성 고정
pip freeze > requirements.txt

# 가상 환경 업데이트
pip install --upgrade -r requirements.txt

Git 고려사항

# .gitignore 파일에 추가할 내용
venv/
__pycache__/
*.pyc
.env
*.egg-info/
build/
dist/

추가 참고 자료

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/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

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.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

관련 스킬