Python Decorators | @decorator Syntax· functools.wraps
이 글의 핵심
Master Python decorators: function decorators, parameterized factories, logging, caching, auth, class decorators, and functools.wraps—with clear examples.
Introduction
“Dressing up functions”
Decorators are a powerful Python feature for adding behavior around functions (or classes).
1. Function decorators basics
A simple function decorator
def timer(func):
"""Measure how long a function runs."""
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.4f}s")
return result
return wrapper
@timer
def slow_function():
import time
time.sleep(1)
return "done"
result = slow_function()
# slow_function took: 1.0012s
Logging decorator
def logger(func):
"""Log function calls."""
def wrapper(*args, **kwargs):
print(f"[call] {func.__name__}({args}, {kwargs})")
result = func(*args, **kwargs)
print(f"[return] {result}")
return result
return wrapper
@logger
def add(a, b):
return a + b
add(3, 5)
# [call] add((3, 5), {})
# [return] 8
2. Decorators with arguments
Decorator factory
def repeat(times):
"""Run the wrapped function multiple times."""
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for _ in range(times):
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@repeat(3)
def greet(name):
return f"Hello, {name}!"
print(greet("Alice"))
# ['Hello, Alice!', 'Hello, Alice!', 'Hello, Alice!']
3. Practical decorators
Memoization (caching)
def memoize(func):
"""Cache function results."""
cache = {}
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(100)) # very fast!
Authentication decorator
def require_auth(func):
"""Require an authenticated user."""
def wrapper(user, *args, **kwargs):
if not user.get('is_authenticated'):
raise PermissionError("Login required")
return func(user, *args, **kwargs)
return wrapper
@require_auth
def delete_post(user, post_id):
return f"Post {post_id} deleted"
# Usage
user = {'name': 'Alice', 'is_authenticated': True}
print(delete_post(user, 123)) # Post 123 deleted
guest = {'name': 'guest', 'is_authenticated': False}
# delete_post(guest, 123) # PermissionError!
4. Class decorators
def singleton(cls):
"""Singleton pattern."""
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
def __init__(self):
print("Database connection")
self.connection = "Connected"
# Usage
db1 = Database() # Database connection
db2 = Database() # no extra print (same instance)
print(db1 is db2) # True
5. functools.wraps
Preserve metadata
from functools import wraps
def my_decorator(func):
@wraps(func) # keep original function metadata
def wrapper(*args, **kwargs):
"""Wrapper docstring."""
return func(*args, **kwargs)
return wrapper
@my_decorator
def greet(name):
"""Greeting function."""
return f"Hello, {name}!"
print(greet.__name__) # greet (without wraps you'd see wrapper)
print(greet.__doc__) # Greeting function.
6. Real-world example: API retry decorator
import time
from functools import wraps
def retry(max_attempts=3, delay=1):
"""Retry on failure."""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
print(f"Attempt {attempt + 1} failed: {e}")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=3, delay=0.5)
def fetch_data(url):
import random
if random.random() < 0.7:
raise ConnectionError("connection failed")
return f"data from {url}"
Practical tips
Decorator patterns
# ✅ Stacking multiple decorators
@timer
@logger
@retry(3)
def important_function():
pass
# Execution order: retry → logger → timer → underlying function
# ✅ Always prefer functools.wraps
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
Summary
Key takeaways
- Decorators wrap functions to add behavior.
- Syntax:
@decorator_nameabovedef. - Parameters: use a decorator factory that returns the real decorator.
- wraps: preserve
__name__,__doc__, and the function module. - Uses: logging, caching, authentication, retries, timing.
Next steps
- Generators (
yield) - Flask web basics
Related posts
- [Python functions | Parameters, return values, lambdas, decorators](/en/blog/python-series-04-functions/
- [Python environment setup | Install Python on Windows and Mac](/en/blog/python-series-01-environment-setup/
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. Master Python decorators: function decorators, parameterized factories, logging, caching, auth, class decorators, and fu… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. Python 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- [Python Functions | Parameters· Return Values](/en/blog/python-series-04-functions/
- [Python Classes | Object-Oriented Programming (OOP) Explained](/en/blog/python-series-05-classes/
- [TypeScript Decorators](/en/blog/typescript-series-06-decorators/
- [JavaScript Classes | ES6 Class Syntax Explained](/en/blog/javascript-series-07-classes/
이 글에서 다루는 키워드 (관련 검색어)
Python, Decorator, functools, Higher-Order Functions, Metaprogramming 등으로 검색하시면 이 글이 도움이 됩니다.