KR_Decorator - somaz94/python-study GitHub Wiki
λ°μ½λ μ΄ν°λ ν¨μλ ν΄λμ€μ λμμ μμ νκ±°λ νμ₯νλ λ°©λ²μ΄λ€.
# κΈ°λ³Έ λ°μ½λ μ΄ν°
def my_decorator(func):
def wrapper():
print("ν¨μ μ€ν μ ")
func()
print("ν¨μ μ€ν ν")
return wrapper
# λ°μ½λ μ΄ν° μ¬μ©
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# μΆλ ₯:
# ν¨μ μ€ν μ
# Hello!
# ν¨μ μ€ν ν
# λ°μ½λ μ΄ν° μλ μ리 μ΄ν΄νκΈ°
# @my_decoratorλ λ€μκ³Ό λμΌνλ€:
# say_hello = my_decorator(say_hello)
β
νΉμ§:
- ν¨μ μμ
- μ½λ μ¬μ¬μ©
- κ°λ μ± ν₯μ
- ν¨μ νμ₯
- μλ³Έ ν¨μ 보쑴
ν¨μμ μΈμκ° μμ λ λ°μ½λ μ΄ν°λ₯Ό μ μ©νλ λ°©λ²μ΄λ€.
def with_params(func):
def wrapper(*args, **kwargs):
print(f"λ§€κ°λ³μ: {args}, {kwargs}")
return func(*args, **kwargs)
return wrapper
@with_params
def add(a, b):
return a + b
print(add(3, 4)) # λ§€κ°λ³μ: (3, 4), {}
# 7
# λ©μλμ μ μ©
class Calculator:
@with_params
def multiply(self, a, b):
return a * b
calc = Calculator()
print(calc.multiply(5, 6)) # λ§€κ°λ³μ: (Calculator κ°μ²΄, 5, 6), {}
# 30
β
νΉμ§:
- μ μ°ν λ§€κ°λ³μ μ²λ¦¬
- ν¨μ λ°νκ° μ²λ¦¬
- λλ²κΉ μ©μ΄
- λͺ¨λ νμ μ μΈμ μ§μ
- ν΄λμ€ λ©μλμλ μ μ© κ°λ₯
λ°μ½λ μ΄ν° μ체μ λ§€κ°λ³μλ₯Ό μ λ¬νμ¬ λμμ 컀μ€ν°λ§μ΄μ§ν μ μλ€.
def repeat(times):
def decorator(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):
import random
greetings = ["Hello", "Hi", "Hey", "Howdy", "Greetings"]
return f"{random.choice(greetings)} {name}"
print(greet("Alice")) # 3κ°μ μΈμ¬λ§ λͺ©λ‘ μΆλ ₯
# λ°μ½λ μ΄ν° μ€μ²© λ§€κ°λ³μ
def log_level(level):
def decorator(func):
def wrapper(*args, **kwargs):
print(f"[{level}] ν¨μ '{func.__name__}' μ€ν")
return func(*args, **kwargs)
return wrapper
return decorator
@log_level(level="INFO")
def process_data(data):
return data * 2
print(process_data(10)) # [INFO] ν¨μ 'process_data' μ€ν
# 20
β
νΉμ§:
- λ°μ½λ μ΄ν° μ€μ
- λ°λ³΅ μ€ν
- 컀μ€ν°λ§μ΄μ§
- μ€μ λ§€κ°λ³μ μ§μ
- μ€μ²© λ°μ½λ μ΄ν° ꡬ쑰
- λ°νμ λ§€κ°λ³μ μ§μ
ν΄λμ€λ₯Ό μ΄μ©ν λ°μ½λ μ΄ν° ꡬν λ°©λ²μ΄λ€.
class Timer:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
import time
start = time.time()
result = self.func(*args, **kwargs)
end = time.time()
self.count += 1
print(f"ν¨μ '{self.func.__name__}'μ {self.count}λ²μ§Έ νΈμΆ, μ€ν μκ°: {end - start:.4f}μ΄")
return result
@Timer
def slow_function():
import time
time.sleep(1)
slow_function() # ν¨μ 'slow_function'μ 1λ²μ§Έ νΈμΆ, μ€ν μκ°: 1.0010μ΄
slow_function() # ν¨μ 'slow_function'μ 2λ²μ§Έ νΈμΆ, μ€ν μκ°: 1.0008μ΄
# ν΄λμ€ μ체μ λ°μ½λ μ΄ν° μ μ©
def singleton(cls):
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, url):
print(f"DBμ μ°κ²° μ€: {url}")
self.url = url
db1 = Database("localhost:5432") # DBμ μ°κ²° μ€: localhost:5432
db2 = Database("localhost:5432") # μΆλ ₯ μμ (κ°μ μΈμ€ν΄μ€ λ°ν)
print(db1 is db2) # True
β
νΉμ§:
- κ°μ²΄μ§ν₯μ μ κ·Ό
- μν μ μ§
- λ©μλ μ¬μ¬μ©
- νΈμΆ νμ μΆμ
- μΈμ€ν΄μ€ μμ± μ μ΄
- ν΄λμ€ λ³ν
μ¬λ¬ λ°μ½λ μ΄ν°λ₯Ό ν¨κ» μ¬μ©νμ¬ κΈ°λ₯μ μ‘°ν©ν μ μλ€.
def bold(func):
def wrapper(*args, **kwargs):
return f"<b>{func(*args, **kwargs)}</b>"
return wrapper
def italic(func):
def wrapper(*args, **kwargs):
return f"<i>{func(*args, **kwargs)}</i>"
return wrapper
@bold
@italic
def hello(name):
return f"Hello, {name}!"
print(hello("World")) # <b><i>Hello, World!</i></b>
# λ°μ½λ μ΄ν° μ μ© μμ μ΄ν΄νκΈ°
# μμͺ½μμ λ°κΉ₯μͺ½ μμλ‘ μ€νλλ€
@bold # 3. bold μ μ©
@italic # 2. italic μ μ©
def goodbye(name):
return f"Goodbye, {name}!" # 1. μλ ν¨μ μ€ν
print(goodbye("Python")) # <b><i>Goodbye, Python!</i></b>
β
νΉμ§:
- λ°μ½λ μ΄ν° 체μ΄λ
- μμ μ€μμ±
- ν μ€νΈ λ³ν μμ
- μ€μ²© μ€ν νλ¦
- λ€μ€ κΈ°λ₯ νμ₯
- μ½λ λͺ¨λν
μ€μ νλ‘μ νΈμμ μ μ©νκ² μ¬μ©ν μ μλ λ°μ½λ μ΄ν° μμ μ΄λ€.
import functools
import time
import logging
# ν¨μ λ©νλ°μ΄ν° 보쑴
def log_execution(func):
@functools.wraps(func) # μλ³Έ ν¨μμ λ©νλ°μ΄ν° μ μ§
def wrapper(*args, **kwargs):
logging.info(f"ν¨μ {func.__name__} μ€ν μμ")
result = func(*args, **kwargs)
logging.info(f"ν¨μ {func.__name__} μ€ν μλ£")
return result
return wrapper
# μ¬μλ λ°μ½λ μ΄ν°
def retry(max_attempts=3, delay=1):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts:
raise
logging.warning(f"μλ {attempt}/{max_attempts} μ€ν¨: {e}")
time.sleep(delay)
return wrapper
return decorator
# μΊμ± λ°μ½λ μ΄ν°
def memoize(func):
cache = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
# ν€μλ μΈμλ μ λ ¬νμ¬ μΌκ΄λ ν΄μ μμ±
key = str(args) + str(sorted(kwargs.items()))
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return wrapper
# νμ
κ²μ¬ λ°μ½λ μ΄ν°
def validate_types(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
sig = func.__annotations__
# μμΉ μΈμ κ²μ¬
for i, (param_name, param_type) in enumerate(
[(k, v) for k, v in sig.items() if k != 'return'][:len(args)]
):
if not isinstance(args[i], param_type):
raise TypeError(f"μΈμ '{param_name}'λ {param_type}μ΄μ΄μΌ νμ§λ§ {type(args[i])}μ
λλ€")
# ν€μλ μΈμ κ²μ¬
for param_name, arg in kwargs.items():
if param_name in sig and not isinstance(arg, sig[param_name]):
raise TypeError(f"μΈμ '{param_name}'λ {sig[param_name]}μ΄μ΄μΌ νμ§λ§ {type(arg)}μ
λλ€")
result = func(*args, **kwargs)
# λ°νκ° κ²μ¬
if 'return' in sig and not isinstance(result, sig['return']):
raise TypeError(f"λ°νκ°μ {sig['return']}μ΄μ΄μΌ νμ§λ§ {type(result)}μ
λλ€")
return result
return wrapper
# λ°μ½λ μ΄ν° μ¬μ© μμ
@log_execution
@retry(max_attempts=3)
@memoize
@validate_types
def fetch_data(url: str, timeout: int = 10) -> dict:
"""μΈλΆ APIμμ λ°μ΄ν°λ₯Ό κ°μ Έμ€λ ν¨μ"""
import random
if random.random() < 0.3: # 30% νλ₯ λ‘ μ€ν¨
raise ConnectionError("μ°κ²° μ€λ₯ λ°μ")
return {"data": f"URL {url}μμ κ°μ Έμ¨ λ°μ΄ν°", "timestamp": time.time()}
# λ‘κΉ
μ€μ
logging.basicConfig(level=logging.INFO)
try:
result = fetch_data("https://api.example.com", timeout=5)
print(result)
# λ λ²μ§Έ νΈμΆ - μΊμμμ κ°μ Έμ΄
result2 = fetch_data("https://api.example.com", timeout=5)
print("μΊμ ννΈ:", result is result2)
# μλͺ»λ νμ
μ λ¬
fetch_data(123) # TypeError λ°μ
except Exception as e:
print(f"μ€λ₯ λ°μ: {e}")
β
νΉμ§:
- ν¨μ λ©νλ°μ΄ν° 보쑴
- μμΈ μ²λ¦¬μ μ¬μλ
- λ©λͺ¨μ΄μ μ΄μ (μΊμ±)
- νμ κ²μ¦
- λ‘κΉ λ° λλ²κΉ
- μ€μ μ μ€μΌμ΄μ€ μ§μ
- ν¨μν νλ‘κ·Έλλ° μ κ·Όλ²
ν΄λμ€ λ΄ λ©μλμ λ°μ½λ μ΄ν°λ₯Ό μ μ©νλ λ°©λ²μ΄λ€.
import functools
# μΈμ€ν΄μ€ λ©μλ λ°μ½λ μ΄ν°
def require_auth(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
if not self.is_authenticated:
raise PermissionError("μΈμ¦μ΄ νμν©λλ€")
return func(self, *args, **kwargs)
return wrapper
# ν΄λμ€ λ©μλ λ°μ½λ μ΄ν°
def class_method_decorator(func):
@functools.wraps(func)
def wrapper(cls, *args, **kwargs):
print(f"ν΄λμ€ {cls.__name__}μ λ©μλ νΈμΆ")
return func(cls, *args, **kwargs)
return wrapper
# μ μ λ©μλ λ°μ½λ μ΄ν°
def static_method_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("μ μ λ©μλ νΈμΆ")
return func(*args, **kwargs)
return wrapper
class User:
def __init__(self, username, authenticated=False):
self.username = username
self.is_authenticated = authenticated
@require_auth
def view_profile(self):
return f"{self.username}μ νλ‘ν μ 보"
@classmethod
@class_method_decorator
def create_admin(cls, username):
user = cls(username, authenticated=True)
user.is_admin = True
return user
@staticmethod
@static_method_decorator
def validate_username(username):
return len(username) >= 3 and username.isalnum()
# λ©μλ λ°μ½λ μ΄ν° μ¬μ©
user = User("guest")
admin = User("admin", authenticated=True)
try:
print(user.view_profile()) # PermissionError λ°μ
except PermissionError as e:
print(f"μ€λ₯: {e}")
print(admin.view_profile()) # adminμ νλ‘ν μ 보
print(User.create_admin("superuser").is_admin) # True
print(User.validate_username("user123")) # True
print(User.validate_username("a")) # False
β
νΉμ§:
- μΈμ€ν΄μ€ λ©μλ μ₯μ
- ν΄λμ€ λ©μλ μ₯μ
- μ μ λ©μλ μ₯μ
- μ κ·Ό μ μ΄ κ΅¬ν
- self/cls λ§€κ°λ³μ μ²λ¦¬
- μμ νΈνμ±
- λ©μλ μ’ λ₯λ³ μ μ© λ°©λ²
λ°μ½λ μ΄ν° νμ©μ λν κ³ κΈ ν
ν¬λκ³Ό λμμΈ ν¨ν΄μ΄λ€.
import functools
import inspect
from typing import Callable, TypeVar, cast, Any
# λ°μ½λ μ΄ν° ν©ν 리 ν¨ν΄
class DecoratorFactory:
@staticmethod
def create_logging_decorator(log_level):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"[{log_level}] ν¨μ νΈμΆ: {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
debug = DecoratorFactory.create_logging_decorator("DEBUG")
info = DecoratorFactory.create_logging_decorator("INFO")
warning = DecoratorFactory.create_logging_decorator("WARNING")
@debug
def test_function():
return "ν
μ€νΈ μλ£"
# λ°μ½λ μ΄ν° ν©μ± (Decorator Composition)
def compose_decorators(*decorators):
def compose(f):
for decorator in reversed(decorators):
f = decorator(f)
return f
return compose
# νμ
ννΈλ₯Ό μ μ§νλ λ°μ½λ μ΄ν° (Python 3.10+)
T = TypeVar('T', bound=Callable[..., Any])
def preserves_signature(decorator: Callable[[T], Callable[..., Any]]) -> Callable[[T], T]:
"""λ°μ½λ μ΄ν°κ° μλ³Έ ν¨μμ νμ
ννΈλ₯Ό μ μ§νλλ‘ νλ€"""
@functools.wraps(decorator)
def wrapped_decorator(func: T) -> T:
decorated_func = decorator(func)
return cast(T, decorated_func)
return wrapped_decorator
@preserves_signature
def timing(func: T) -> Callable[..., Any]:
@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
import time
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"ν¨μ {func.__name__} μ€ν μκ°: {end - start:.4f}μ΄")
return result
return wrapper
# λμ λ°μ½λ μ΄ν° μμ±
def create_validator(validation_func, error_message):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if not validation_func(*args, **kwargs):
raise ValueError(error_message)
return func(*args, **kwargs)
return wrapper
return decorator
# λ°μ½λ μ΄ν° μ€ν κ²μ¬
def get_decorator_stack(func):
"""ν¨μμ μ μ©λ λ°μ½λ μ΄ν° μ€νμ λΆμνλ€"""
result = []
current = func
while hasattr(current, '__wrapped__'):
result.append(current.__name__)
current = current.__wrapped__
result.append(current.__name__)
return result
# λ©μλ 체μ΄λμ μν λ°μ½λ μ΄ν°
def chainable(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
func(self, *args, **kwargs)
return self
return wrapper
# μ¬μ© μμ
class QueryBuilder:
def __init__(self):
self.filters = []
self.sorts = []
@chainable
def filter(self, **kwargs):
self.filters.append(kwargs)
@chainable
def sort_by(self, field, ascending=True):
self.sorts.append((field, ascending))
def execute(self):
return f"Query with filters: {self.filters} and sorts: {self.sorts}"
query = (QueryBuilder()
.filter(status="active")
.filter(age__gt=18)
.sort_by("created_at", ascending=False)
.execute())
print(query)
β
νΉμ§:
- λ°μ½λ μ΄ν° ν©ν 리 ν¨ν΄
- λ°μ½λ μ΄ν° ν©μ±
- νμ ννΈ λ³΄μ‘΄
- λμ λ°μ½λ μ΄ν° μμ±
- λ°μ½λ μ΄ν° μ€ν λΆμ
- λ©μλ 체μ΄λ ꡬν
- κ³ κΈ μ€κ³ κΈ°λ²
β
λͺ¨λ² μ¬λ‘:
- functools.wraps νμ μ¬μ©νμ¬ λ©νλ°μ΄ν°(docstring, μ΄λ¦, μλͺ ) 보쑴
- λ¬Έμν μ μ§ λ° λ°μ½λ μ΄ν° μ체μ docstring μΆκ°
- μλ¬ μ²λ¦¬ ꡬννμ¬ λͺ νν μ€λ₯ λ©μμ§ μ 곡
- μ±λ₯ κ³ λ € (λΆνμν μ€λ²ν€λ νΌνκΈ°)
- κ°λ μ± μ μ§ (볡μ‘ν λ°μ½λ μ΄ν°λ μμ λ¨μλ‘ λΆν΄)
- ν μ€νΈ μμ± (λ°μ½λ μ΄ν°μ μ₯μλ ν¨μ λͺ¨λ ν μ€νΈ)
- μ¬μ¬μ©μ± κ³ λ € (λ²μ©μ κΈ°λ₯μΌλ‘ μ€κ³)
- νμ ννΈμ νΈνμ± μ μ§
- μΈμ μλ λ°μ½λ μ΄ν°μ μΈμ μλ λ°μ½λ μ΄ν° κ΅¬λΆ μ΄ν΄
- λ°μ½λ μ΄ν° λ¨κ³λ³ λλ²κΉ λ°©λ² μμ§
- ν΄λμ€ κΈ°λ°κ³Ό ν¨μ κΈ°λ° λ°μ½λ μ΄ν°μ μ μ ν μ¬μ© μν© μ΄ν΄
- κ³Όλν λ°μ½λ μ΄ν° μ€μ²© νΌνκΈ° (3-4κ° μ΄μ μ€μ²© μ 볡μ‘λ μ¦κ°)
- νμν κ²½μ° inspection λͺ¨λλ‘ ν¨μ μλͺ λΆμ