본문 바로가기

프로그래밍언어/Python

[Python] Decorator

반응형

1. Decorator 란?

데코레이터란 또 다른 함수를 반환하는 함수를 의미한다. @wrapper 문법으로 다른 함수를 감싸는 방법으로 적용된다. 감싸진 함수는 wrapper 함수에 인자로 전달되고, wrapper 함수 내부에서 실행된다.


주로 함수의 내용을 직접 변경하지 않고 특정 행동이나 동작을 추가하기 위해서 사용하며, 많이 사용하는 데코레이터로는 classmethod() 와 staticmethod() 가 있다.

데코레이터는 다음과 같이 사용할 수 있으며, 예시의 두가지 표현은 동일하게 동작한다.

 

# 함수 호출로 적용
def func(arg):
	statement(s)
func = wrapper(func)

# 데코레이터 표현식 적용
@wrapper
def func(arg):
	statement(s)


함수를 정의할 때, 하나 이상의 데코레이터로 해당 함수를 감쌀 수 있다. 데코레이터 표현식은 함수가 정의될 때, 정의된 함수를 포함하는 영역에서 한번 실행된다. 그 결과는 callable 이어야 하는데, 데코레이터는 정의된 함수 객체를 인자로 받아 호출된다. 여러개의 데코레이터가 지정되는 경우에는 중첩이 된다.

 

@deco1
@deco2
def func():
	pass

# 위의 func 는 아래 표현과 동일하다.
func = deco1(deco2(func()))


데코레이터 함수에서는 주로 inner function 을 정의하여 사용하는 경우가 많다. inner function 을 사용하면 해당 함수의 인자로 전달된 값들을 사용할 수 있게된다.

 

def deco(func):
    # args 와 kwargs 는 func 메서드의 인자가매개 된다.
    def wrap(*args, ** kwargs):
        func(*args, **kwargs)
    return wrap

@deco
def func(val):
	print(val)

# func(4) -> deco(func)(4) -> wrap(4) -> func(4)
func(4) # 4


※ Python 에서 함수는 1급 객체이다. 이 말은 함수를 다른 객체와 같이 참조 변수에 저장하거나 인자로 넘겨줄 수 있다는 의미이다. 함수가 객체로 다루어지기 때문에 데코레이터에서 함수를 인자로 넘겨주고 이를 받아서 사용할 수 있다.


2. 데코레이터에서 인자 사용

데코레이터에 인자를 사용할 수 있다. 데코레이터에 인자를 넣어주면 데코레이터 함수가 인자를 받아서 실행한 결과를 반환한다. 물론 이 결과도 callable 한 객체이기 때문에 해당 객체의 인자로 데코레이터를 지정한 함수가 입력되어 동작한다.

 

def deco(val):
    def inner(func):
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs) * val
            print(result)
    return wrapper
return inner

@deco(3)
def func(val):
return val

# deco(3)(func)(3) -> inner(func)(3) -> wrapper(3)
func(3) # 9

 


3. 클래스로 데코레이터 작성

클래스로도 테코레이터를 작성할 수 있다.
앞에서 설명한 것과 같이 파이썬에서는 함수도 객체이기 때문에 클래스의 __init__(), __call__() 등의 메서드와 함께 사용하면 데코레이터로 적용할 수 있다.

 

class Power:
    # 클래스 객체 생성 시에 함수 객체를 저장한다.
    def __init__(self, func):
	    self.func = func

    # 객체 호출 시에 저장된 함수 객체에 인자를 주어서 실행한다.
    def __call__(self, *args, **kwargs):
        result = self.func(*args, **kwargs)
        return result ** 2

@Power
def func(a, b):
return a * b

print(func(1, 2)) # 4


위와같이 생성자와 호출자를 사용하여 클래스 데코레이터를 작성하고 사용할 수 있다. 만약에 데코레이터에 직접 인자를 추가하기 위해서는 내부 함수를 하나 더 추가하면 된다. 예시는 다음과 같다.

 

class Power:
    # 클래스 객체 생성 시에 인자 값을 저장한다.
    def __init__(self, val):
	    self.val = val

    # 객체 호출 시에 함수 객체를 저장하고, 실제 로직이 실행 될 callable 을 반환한다.
    def __call__(self, func):
        self.func = func
        return inner

    # 실제 동작할 로직이다.
    # 입력받아 저장한 인자 값과 함수 객체를 사용하여 연산한 결과를 반환한다.
    def inner(*args, **kwargs):
	    return self.num * self.func(*args, **kwargs)

@Power
def func(a, b):
return a * b

print(func(1, 2)) # 6

 

 

[reference]
https://docs.python.org/3/glossary.html#term-decorator
https://docs.python.org/3/reference/compound_stmts.html#function-definitions
https://www.geeksforgeeks.org/function-wrappers-in-python/
https://www.geeksforgeeks.org/decorators-in-python/

 

Function Wrappers in Python - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

8. Compound statements — Python 3.10.4 documentation

8. Compound statements Compound statements contain (groups of) other statements; they affect or control the execution of those other statements in some way. In general, compound statements span multiple lines, although in simple incarnations a whole compou

docs.python.org

 

Glossary — Python 3.10.4 documentation

The implicit conversion of an instance of one type to another during an operation which involves two arguments of the same type. For example, int(3.15) converts the floating point number to the integer 3, but in 3+4.5, each argument is of a different type

docs.python.org

 

반응형

'프로그래밍언어 > Python' 카테고리의 다른 글

[Python] GIL (Global Interpreter Lock)  (0) 2022.07.24
[Python] Awaitable  (0) 2022.04.19
[Python] module, package, library  (0) 2022.02.11
[Python] copy  (0) 2022.02.08
[Python] Magic method - 속성 관리  (0) 2021.12.16