728x90
Python 코드를 보다 보면 종종 함수나 메서드 위에 @ 기호로 시작하는 이상한(?) 줄을 보게 됩니다. 예를 들면 다음과 같은 형태입니다:
@my_decorator
def my_function():
...
이것은 데코레이터(decorator)라는 문법으로, 기존 함수나 클래스의 동작을 수정하거나 확장할 수 있게 해주는 Python의 강력한 기능입니다. 본 글에서는 데코레이터의 개념, 기본 사용법, 자주 쓰는 내장 데코레이터들, 그리고 직접 만드는 방법을 다루려고 합니다.
데코레이터란?
데코레이터는 다른 함수를 인자로 받아 새로운 기능을 추가한 뒤 반환하는 함수입니다. 흔히 함수에 어떤 "포장"을 입혀주는 역할을 합니다.
예를 들어, 아래와 같은 데코레이터 함수가 있다고 합시다:
def my_decorator(func):
def wrapper():
print("함수 실행 전")
func()
print("함수 실행 후")
return wrapper
이제 이 데코레이터를 @my_decorator로 사용하면:
@my_decorator
def say_hello():
print("안녕하세요!")
위 코드는 아래와 동일합니다:
def say_hello():
print("안녕하세요!")
say_hello = my_decorator(say_hello)
즉, say_hello()를 실행하면 이렇게 출력됩니다:
함수 실행 전
안녕하세요!
함수 실행 후
함수에 인자가 있을 경우
함수에 인자가 있는 경우, *args와 **kwargs를 이용하면 데코레이터를 더 일반화할 수 있습니다.
def my_decorator(func):
def wrapper(*args, **kwargs):
print("함수 실행 전")
result = func(*args, **kwargs)
print("함수 실행 후")
return result
return wrapper
@my_decorator
def add(a, b):
return a + b
print(add(3, 4))
출력 결과:
함수 실행 전
함수 실행 후
7
자주 사용하는 내장 데코레이터
@staticmethod: 인스턴스 없이 호출 가능한 정적 메서드
class MathUtil:
@staticmethod
def add(a, b):
return a + b
# 인스턴스를 만들지 않아도 호출 가능
result = MathUtil.add(3, 5)
print(result) # 출력: 8
@classmethod: 클래스 자신(cls)을 첫 번째 인자로 받는 메서드
class Person:
count = 0
def __init__(self, name):
self.name = name
Person.count += 1
@classmethod
def how_many(cls):
return f"현재 인원 수: {cls.count}"
p1 = Person("Alice")
p2 = Person("Bob")
print(Person.how_many()) # 출력: 현재 인원 수: 2
@property: 메서드를 속성처럼 접근 가능하게 만듦
class MyClass:
@property
def name(self):
return "홍길동"
obj = MyClass()
print(obj.name) # name()이 아닌 name으로 호출
@functools.lru_cache: 함수 결과를 캐싱하여 성능 향상
import functools
@functools.lru_cache(maxsize=None)
def fib(n):
if n <= 1:
return n
return fib(n - 1) + fib(n - 2)
print(fib(30)) # 매우 빠르게 계산됨 (결과: 832040)
데코레이터에 인자 전달하기 (중첩 함수)
데코레이터 자체에 인자를 넘기고 싶을 때는 한 번 더 함수를 감싸면 됩니다.
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def greet():
print("안녕하세요!")
결과:
안녕하세요!
안녕하세요!
안녕하세요!
functools.wraps를 꼭 써야 할까?
Python의 functools 모듈에서 @wraps 데코레이터를 사용하면, 원래 함수의 이름, docstring 등을 유지할 수 있습니다.
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
이렇게 하지 않으면 데코레이터가 적용된 함수의 이름이나 문서가 모두 wrapper로 바뀌기 때문에 디버깅이나 문서화에서 문제가 생길 수 있습니다.
감사합니다!
728x90
'프로그래밍 > Python' 카테고리의 다른 글
| Python 하위/상위 폴더의 함수 import하는 법 (3) | 2025.07.31 |
|---|---|
| Python lambda 함수란 (4) | 2025.07.16 |
| Python *args, **kwargs 의미 (20) | 2025.07.14 |
| Python expand() 및 repeat() (16) | 2025.07.12 |
| Python squeeze 및 unsqueeze 함수 - view와의 차이 (4) | 2025.07.12 |