함수 객체의 다양한 활용 방식
파이썬에서 함수는 일급 객체(first-class object)로서 변수에 할당하거나, 컨테이너에 저장하며, 인자로 전달하거나 반환값으로 사용할 수 있습니다.
함수를 변수에 할당
def add(x, y):
print(x + y)
operation = add
operation(15, 25) # 출력: 40
함수를 컨테이너에 저장
def multiply(x, y):
return x * y
operations = {'multiply': multiply}
result = operations['multiply'](7, 8)
print(result) # 출력: 56
함수를 다른 함수의 인자로 전달
def subtract(x, y):
return x - y
def execute(a, b, operation_func):
return operation_func(a, b) + a
outcome = execute(10, 3, subtract)
print(outcome) # 출력: 17
함수를 반환값으로 사용
def get_calculator():
def divide(x, y):
return x / y if y != 0 else None
return divide
calc = get_calculator()
print(calc(20, 4)) # 출력: 5.0
클로저(Closure)
클로저란 외부 함수의 지역 변수를 내부 함수가 참조하고 있는 상태를 말합니다. 이 구조는 데이터 캡슐화와 상태 유지에 유용합니다.
기본적인 클로저 예제
def outer_scope():
counter = 0
def inner_logic():
nonlocal counter
counter += 1
print(f"호출 횟수: {counter}")
return inner_logic
counter_fn = outer_scope()
counter_fn() # 호출 횟수: 1
counter_fn() # 호출 횟수: 2
동적 파라미터를 사용한 클로저
def create_greeting(prefix):
def greet(name):
print(f"{prefix}, {name}!")
return greet
hello = create_greeting("안녕하세요")
hi = create_greeting("반갑습니다")
hello("지훈") # 안녕하세요, 지훈!
hi("민수") # 반갑습니다, 민수!
데코레이터(Decorator)
데코레이터는 기존 함수의 코드나 호출 방식을 변경하지 않고도 새로운 동작을 추가할 수 있는 강력한 패턴입니다. 주로 로깅, 인증, 성능 측정 등에 사용됩니다.
실행 시간 측정 데코레이터
import time
def timing_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} 실행 시간: {end_time - start_time:.4f}s")
return result
return wrapper
@timing_decorator
def slow_task():
time.sleep(1)
print("작업 완료")
slow_task()
로그인 인증 데코레이터
user_session = {'logged_in': False, 'username': None}
def require_auth(func):
def secure_wrapper(*args, **kwargs):
if not user_session['logged_in']:
return False, "접근 거부: 로그인이 필요합니다."
return func(*args, **kwargs)
return secure_wrapper
@require_auth
def withdraw_money(amount):
print(f"{amount}원 출금 처리 중...")
return True, "출금 완료"
# 세션 미인증 상태
print(withdraw_money(50000)) # (False, '접근 거부: 로그인이 필요합니다.')
# 인증 후
user_session['logged_in'] = True
user_session['username'] = 'james'
print(withdraw_money(50000)) # 출금 처리됨
다중 데코레이터 적용
여러 데코레이터를 동시에 사용할 수 있으며, 위에서 아래로 순서대로 감쌉니다.
def log_calls(func):
def logger(*args, **kwargs):
print(f"[LOG] 호출됨: {func.__name__}")
return func(*args, **kwargs)
return logger
def enforce_admin(func):
def admin_guard(*args, **kwargs):
if user_session.get('role') != 'admin':
return False, "권한 부족: 관리자만 접근 가능"
return func(*args, **kwargs)
return admin_guard
@log_calls
@enforce_admin
def delete_user(user_id):
print(f"사용자 {user_id} 삭제")
return True, "삭제 성공"
user_session['role'] = 'user'
print(delete_user(123))
# [LOG] 호출됨: delete_user
# (False, '권한 부족: 관리자만 접근 가능')
매개변수가 있는 데코레이터
외부 설정이나 조건에 따라 다르게 동작하는 데코레이터를 만들 수 있습니다.
def retry_on_failure(max_attempts=3):
def decorator(target_func):
def handler(*args, **kwargs):
for attempt in range(1, max_attempts + 1):
try:
return target_func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts:
print(f"최대 재시도 초과: {e}")
raise
print(f"시도 {attempt} 실패, 재시도 중...")
return handler
return decorator
@retry_on_failure(max_attempts=2)
def unstable_api_call():
import random
if random.choice([True, False]):
raise ConnectionError("네트워크 오류")
print("API 호출 성공")
# 여러 번 실행해보면 실패 후 재시도되는 것을 확인할 수 있음
데코레이터 메타데이터 보존
데코레이터를 사용하면 원본 함수의 이름, 설명문(docstring), 매개변수 정보 등이 손실될 수 있습니다. 이를 방지하기 위해 functools.wraps를 사용합니다.
from functools import wraps
def enhanced_logger(func):
@wraps(func)
def logged_function(*args, **kwargs):
"""모든 함수 호출을 기록하는 래퍼"""
print(f"실행: {func.__name__}")
return func(*args, **kwargs)
return logged_function
@enhanced_logger
def sample_task():
"""샘플 작업을 수행합니다."""
pass
print(sample_task.__name__) # sample_task (wraps 없으면 logged_function)
print(sample_task.__doc__) # 샘플 작업을 수행합니다.