본문 바로가기
Python

파이썬 데코레이터

by 승환파크 2023. 5. 9.

함수를 수정하지 않은 상태에서 추가 기능을 구현할 때 사용한다.

def hello():
    print("hello 함수 시작")
    print("hello")
    print("hello 함수 끝")
    
def world():
    print("world 함수 시작")
    print("world")
    print("world 함수 끝")

hello()
# 결과값
# hello 함수 시작
# hello
# hello 함수 끝

world()
# 결과값
# world 함수 시작
# world
# world 함수 끝

위 코드와 같이 "(함수명) 함수시작" 이라는 문자열과 "(함수명) 함수 끝" 이라는 문자열을 출력하고 싶을 때 일일히 코드를 작성하는 것보다 데코레이터를 사용하면 더욱 쉽게 사용할 수 있고 코드의 재사용이 가능하다는 장점이 있다.

def trace(func): 						# 호출할 함수를 매개변수로 받음
    def wrapper(): 						# 호출할 함수를 감싸는 함수
        print(func.__name__, "함수 시작") # __name__으로 함수 이름 출력
        func() 							# 매개변수로 받은 함수를 호출
        print(func.__name__, "함수 끝")  # __name__으로 함수 이름 출력
    return wrapper 					   # wrapper 함수 반환

def hello():
	print("hello")
    
def world():
	print("world")

trace_hello = trace(hello) # 데코레이터에 호출할 함수를 넣음
trace_hello() # 반환된 함수를 호출
# 결과값
# hello 함수 시작
# hello
# hello 함수 끝

trace_world = trace(world) # 데코레이터에 호출할 함수를 넣음
trace_world() # 반환된 함수를 호출
# 결과값
# world 함수 시작
# world
# world 함수 끝

 

 

@로 데코레이터 사용하기

def trace(func):                        
    def wrapper():                      
        print(func.__name__, "함수 시작") 
        func()
        print(func.__name__, "함수 끝")
    return wrapper 

@trace # @ 데코레이터
def hello():
	print("hello")
    
@trace
def world():
	print("world")

# 함수를 그대로 사용
hello()
# 결과값
# hello 함수 시작
# hello
# hello 함수 끝

world()
# 결과값
# world 함수 시작
# world
# world 함수 끝

함수 명은 바꿔도 상관은 없다.

 

데코레이터 여러개 지정하기

def decorator1(func):
    def wrapper():
        print("decorator1")
        func()
    return wrapper

def decorator2(func):
    def wrapper():
        print("decorator2")
        func()
    return wrapper

@decorator1
@decorator2
def hello():
	print("hello")

hello()
# 결과값
# decorator1
# decorator2
# hello
# 데코레이터 2개를 사용했는데 hello가 한개만 찍힌다
# 과정순서가 decorator1(decorator2(hello)) 순으로 진행되는것 같다

 

 

매개변수와 반환값을 처리하는 데코레이터

def trace(func):
    def wrapper(a, b):
        res = func(a, b) # fucn에 매개변수 a 와 b를 넣어서 호출하고 반환값을 변수에 저장
        print(f"{func.__name__}(a = {a}, b = {b}) -> {res}")
        return res # func의 반환값을 반환
    return wrapper

@trace
def add(a, b):
	return a + b

print(add(10, 20))
# 결과값
# add(a = 10, b = 20) -> 30
# 30

wrapper의 파라미터 a, b는 파이썬에서 자동으로 add(a, b)로 들어간 파라미터로 값을 넣어준다.

trace 쪽 func 에는 함수를 전달, wrapper 에는 인수를 전달한다.

그 이후 wrapper 에서 func 파라미터로 전달받은 wrapper 파라미터의 값을 넣어준다.

 

 

가변 인수 함수 데코레이터

def trace(func):
    def wrapper(*args, **kwargs): # 가변 인수 함수로 만듦
        res = func(*args, **kwargs)
        print(f"{func.__name__}(args = {args}, kwargs={kwargs}) -> {res}")
        return res
    return wrapper

@trace
def get_max(*args):
    return max(args)

@trace
def get_min(**kwargs):
    return min(kwargs.values())

print(get_max(10, 20, 30))
# 결과값
# get_max(args = (10, 20, 30), kwargs={}) -> 30
# 30

print(get_min(x = 10, y = 20, z = 30))
# 결과값
# get_min(args = (), kwargs={'x': 10, 'y': 20, 'z': 30}) -> 10
# 10

위치인수와 키워드 인수를 모두 처리할 수 있기 때문에 일반적인 함수에서도 사용이 가능하다.

def trace(func):
    def wrapper(*args, **kwargs): # 가변 인수 함수로 만듦
        res = func(*args, **kwargs)
        print(f"{func.__name__}(args = {args}, kwargs={kwargs}) -> {res}")
        return res
    return wrapper

@trace
def add(a, b):
	return a + b

# 결과값
# add(args = (10, 20), kwargs={}) -> 30
# 30

 

 

클래스 메서드에 데코레이터 사용하기

클레스에서 데코레이터를 사용할 때는 self 에 주의해야 한다.

def trace(func):
    def wrapper(self, a, b): # 첫번째 매개변수는 self로 지정
        r = func(self, a, b) # self와 매개변수르 그대로 넣어줌
        print(f"{func.__name__}(a = {a}, b = {b}) -> {r}")
        return r
    return wrapper

class Calc:
	@trace
    def add(self, a, b):
    	return a + b

c = Calc()
print(c.add(10, 20))
# 결과값
# add(a = 10, b = 20) -> 30
# 30

 

 

클래스로 데코레이터 만들기

인스턴스를 함수처럼 호출하게 해주는 __call__ 메서드를 구현해야 한다.

class Trace:
    def __init__(self, func): # 호출할 함수를 인스턴스의 초기값으로 받음
        self.func = func # 호출할 함수를 인스턴스 변수 func에 저장
        
    def __call__(self):
        print(self.func.__name__, "함수 시작")
        slef.func()
        print(self.func.__name__, "함수 끝")

@Trace
def hello():
	print("hello")

hello()
# 결과값
# hello 함수 시작
# hello
# hello 함수 끝

@를 지정하지 않고 데코레이터의 반환값을 호출하는 방식으로도 사용이 가능하다.

class Trace:
    def __init__(self, func): # 호출할 함수를 인스턴스의 초기값으로 받음
        self.func = func # 호출할 함수를 인스턴스 변수 func에 저장
        
    def __call__(self):
        print(self.func.__name__, "함수 시작")
        self.func()
        print(self.func.__name__, "함수 끝")

def hello():
	print("hello")

trace_hello = Trace(hello) # 데코레이터에 호출할 함수를 넣어서 인스턴스 생성
trace_hello() # 인스턴스를 호출, __call__ 메서드가 호출됨
# 결과값
# hello 함수 시작
# hello
# hello 함수 끝

 

 

클래스로 매개변수와 반환값을 처리하는 데코레이터

class Trace:
    def __init__(self, func):
        self.func = func
        
    def __call__(self, *args, **kwargs): # 호출할 함수의 매개변수를 처리
        # self.func에 매개변수에 넣어서 호출하고 반환값을 변수에 저장
        r = self.func(*args, **kwargs)
        print(f"{self.func.__name__}(args = {args}, kwargs = {kwargs}) -> {r}")
        return r

@Trace
def add(a, b):
	return a + b
    
print(add(10, 20))
# 결과값
# add(args = (10, 20), kwargs = {}) -> 30
# 30

print(add(a = 10, b = 20))
# 결과값
# add(args = (), kwargs = {'a': 10, 'b': 20}) -> 30
# 30

 

 

클래스로 매개변수가 있는 데코레이터 만들기

class IsMultiple:
    def __init__(self, x): # 데코레이터가 사용할 매개변수를 초기값으로 받음
        self.x = x # 매개변수를 속성 x 에 저장
        
    def __call__(self, func): # 호출할 함수를 매개변수로 받음
        def wrapper(a, b):
            r = func(a, b)
            if r % self.x == 0: # func의 반환값이 self.x의 배수인지 확인
                print(f"{func.__name__}의 반환값은 {self.x}의 배수 입니다.")
            else:
                print(f"{func.__name__}의 반환값은 {self.x}의 배수가 아닙니다.")
            return f
        return wrapper

@IsMultiple(3) # @데코레이터(인수)
def add(a, b):
    return a + b

print(add(10, 20))
# 결과값
# add의 반환값은 3의 배수 입니다.
# 30

print(add(2, 5))
# 결과값
# add의 반환값은 3의 배수가 아닙니다.
# 7

'Python' 카테고리의 다른 글

파이썬 소멸자  (0) 2023.05.09
파이썬 모듈 생성  (0) 2023.05.09
파이썬 제너레이터  (0) 2023.05.09
파이썬 람다함수  (0) 2023.05.09
파이썬 재귀함수  (0) 2023.05.09