[PYTHON] Decorator

김광일·2022년 2월 24일
0

PYTHON

목록 보기
11/13
post-thumbnail

django를 공부하다 보니 decorator를 사용해야 하는 순간이 왔다. 따로 정리를 해놓지 않아 분명 공부했음에도 기억이 가물가물해졌음을 느꼈고, 앞으로는 잊더라도 다시 글을 읽어보며 상기하기 위해 포스팅한다.


decorator

1. what?

@decorator
def function():
	print("what is decoratot?")

decorator, 말 그대로 꾸며준다는 것이다. 위와 같은 형태로 작성할 수 있으며, @decorator 아래에 있는 함수를 wrapping 하고, 그 함수의 앞뒤에 추가적으로 넣을 구문들을 정의하여 사용한다.
또한 이는 비슷한 작업이 반복되는 경우 재사용함으로써 유지보수에 유리하다.


2. how?

decorator가 어떻게 돌아가는지 보기 위한 간단한 예제를 먼저 보자.

def decorator(func):
	def wrapper(*args, **kwargs):
    	print("Hi")  # 전처리 (앞에 추가할 내용)
        print(func(*args, **kwargs))
        print("How are you?") # 후처리 (뒤에 추가할 내용)
   
   
@decorator
def gwangFunc():
	return "My name is Gwang"
> Hi
My name is Gwang
How are you?

이처럼 내가 지정해준 기능과, 앞으로 사용될 func을 조정하여 사용할 수 있다.
만약 100개의 함수에 동일한 기능을 3개씩 반복하여 사용해야 한다고 볼 때, 내용이 수정되면 300개의 줄을 수정해야하지만 decorator를 사용하게 되면 def decorator 내에 있는 부분만 수정해주면 된다. 그로 인해 가독성도 좋아지게 된다.
참고로 메소드명이 꼭 decorator일 필요는 없다. def hi, def abcd.. 무엇이든 사용하여도 되고, @hi, @abcd 이렇게 사용을 하면 되지만, 가독성을 위해 함수 기능과 decorator를 같이 써주는 것이 좋지 않을까 싶다.


3. why?

그렇다면 decorator를 사용하는 이유는 단순히 유지보수만을 위해서일까?
비슷한 결이지만 시스템이 커질수록 관리하기 편하고, 소스코드나 주요 로직을 따로 볼 수 있기 때문에 가독성이 좋아진다.


+ tip!

tip (1)

: @decorator를 사용하지 않아도 decorator를 사용할 수 있다.

def decorator(func):
	def wrapper():
    	print("Yeah~")
        func()
    return wrapper
    
def emotion():
	print("honey~ honey~")
    
emotion()
> honey~ honey~

# print(emotion())
# > honey~ honey~
# None

decoration(emotion)()
> Yeah~
honey~ honey~

decorator = decoration(emotion)
decorator()
> Yeah~
honey~ honey~

이건 좀 다른 내용이지만 emotion() 함수를 호출하면 honey~ honey~~라는 구문이 잘 나온다. 하지만 print(emotion())을 하게 되면 honey~ honey~~ 값과 함께 None 값이 출력된다.
이는 왜 그런가보니, emotion() 함수를 그냥 호출하였을 땐, 그 안에 있는 print() 값만 실행을 하면 되지만, print(emotion())을 하게 되면 일단 emotion()의 내용을 먼저 호출하게 되고, 후에 print() 함수에 따라 return 값을 찾게 되는데 따로 지정해준 return 값이 없으므로 None이 반환되는 것이다. 별 거 아닌 듯 하지만 꽤 큰 차이가 있는 것 같다.

tip (2)

: decorator를 2개 사용하게 되면 어떻게 되는가?

def a(func):
	def wrapper(*args, **kwargs):
    	print("a" * 5)
        func(*args, **kwargs)
    	print("a" * 5)
    return wrapper

def b(func):
	def wrapper(*args, **kwargs):
    	print("b" * 5)
        func(*args, **kwargs)
    	print("b" * 5)
    return wrapper

@a
@b
def test(msg):
	print(msg)
    
test("두근두근")
> aaaaa
bbbbb
두근두근
bbbbb
aaaaa

먼저 쓰인 @a가 그 아래에 있는 @b를 func으로 인식하여 감싸게 되고 @b가 그 아래에 있는 def test()를 감싸는 모습을 보인다.


내가 참고한 사이트
참고 1
참고 2
참고 3
* 참고 4

profile
부족함 없이 공부하자

0개의 댓글