TIL(9) - Decorator
Decorator 학습
Python 에서 decorator는 어떤 역할을 하는지 탐구해보자.
데코레이터(decorator)란 무엇일까?
- 배경: 파이썬에서 객체는 변수에 저장된 주소로 호출된다. 그렇기 때문에 함수객체를 담은 변수(주소)를 다른 함수에 인자로 넘겨서 호출하는것이 가능해진다.
- 정의: 위 원리를 따라서 함수를 인자값으로 받아 명령을 추가한 뒤 이를 다시 함수의 형태로 반환하는 함수이다. 함수의 내부 수정 없이 기능에 변화를 가미할 때 사용하며 일반적으로 함수의 앞뒤 수식추가가 필요할 때 사용한다. 아울러, 데코레이터를 이용해, 반복을 줄이고 메소드나 함수의 책임을 확장한다.
- 아래 다양한 데코레이터 용법을 보면서 어떻게 기능하는지 확인해보자.
<함수의 객체 성질을 보여주는 코드>
def func():
return 1
print(func())
print(func)
- 위 두 print출력문의 차이점: 소괄호의 유무 차이로 인해서; 위는 함수를 실행한 것이고, 아래는 함수 위치를 알려 주는 것으로 실행됨.
- 따라서 print(func()) 는 1이라는 반환값을 출력하고, print(func)는 <function func at 0x7fc83001df70>를 출력함.
- 앞서 설명한 함수가 객체 성질을 보유했다는 사실을 보여줌.
<함수안에 함수 구조의 코드>
def hello(name='Ray Kim'):
print('The hello() function has been executed')
def greet():
return '\t This is the greet() func inside hello'
def welcome():
return '\t This is welcome() inside hello'
if name == 'Ray Kim':
return greet
else:
return welcome
my_new_func = hello('Ray Kim')
- hello() 함수 내부에 존재한 greet()과 welcome() 함수는 hello() 내에서만 호출되는 범위가 제한된 함수이다. 함수 범위가 정해져있기 때문에 특별한 조치 없이 일반 함수 처럼 hello() 함수를 호출하려하면 에러가 난다.
- 위 에러에 대한 해결책으로 함수를 함수의 인자값으로 넣어주는 방법이 있다. 위 코드 예시처럼, "my_new_func = hello('Ray Kim')"에서 hello 함수에 대한 인자값이 'Ray Kim' 임으로 위 코드의 if절을 받기 때문에; 1차로는 hello() 함수를, 2차로는 greet() 함수를 받게된다. hello 함수 밖에서 greet 함수를 호출 할 수 있는 방법 중 하나; 함수 내부에 존재한 함수를 호출할 때 쓰는 방법.
<상위 함수를 그냥 지나치는 함수 코드>
def cool():
def super_cool():
return ' I am very cool!'
return super_cool
some_func = cool()
some_func #cool()
- some_func 변수를 Cool() 함수로 포인팅을 했지만 cool은 그저 Passing 하는 상위 함수 일뿐, 특별한 역할이 부여된 것이 없으므로 그 내부의 super_cool 함수를 반환하게 된다.
<함수A가 동일 선상에서 정의된 다른 함수B를 인자값으로 쓰는 코드>
def hey():
return 'Hey Ray!'
def other(some_def_func):
print('Other code runs here!')
print(some_def_func())
print(id(hey))
print(id(other))
hey()
other(hey)
- 위 코드 출력 결과물:
"Other code runs here!"
"Hey Ray!"
=> 위 코드는 함수A가 다른 함수B의 인자값으로 들어가 함수B의 명령을 행하며 본래 자기 자신 내에 코드를 실행하는 법을 보여준다.
=> 위 출력 과정을 자세히 살펴보면... aaa
(1) 'other(hey)' 실행시, 먼저 정의된 hey()함수가 other()함수의 인자값으로 들어가고,
(2) other()함수의 첫번째 내부 명령인 'other code runs here!'을 출력한 후,
(3) 두번째 내부 명령인 print(some_def_func())함수에 인자값으로 들어가게 되어 print(hey()) 로 변환된 함수를 수행하며 "hey ray!"를 출력하게됨.
<전후를 감싸는(wrap)기능 함수 + @ decorator 기능이 사용된 코드>
def new_decorator(original_func):
def wrap_func():
print('Some extra code, before the original function')
original_func()
print('some extra code, after the original function!')
return wrap_func
def func_needs_decorator1():
print("I want to be decorated!")
func_needs_decorator1()
decorated_func = new_decorator(func_needs_decorator1)
decorated_func()
@new_decorator
def func_needs_decorator2():
print("I want to be decorated!")
func_needs_decorator2()
-
a. Wrap 함수 개념 설명: 위 코드 예제는 "wrap_func()" 함수를 활용해 원하는 출력물의 전후를 감싸는 모양의 다음과 같은 출력물이 나온다:
["Some extra code, before the original function!"
"I want to be decorated!"
"some extra code, after the original function!"]==> new_decorator() 함수의 인자값으로 포장 함수의 내부 내용물이 될 실행할 함수 func_needs_decorator1()를 넣은 후, wrap_func() 포장 함수 출력문('Some extra code...이하 생략')이 앞뒤로 출력된다.
- b. @ decorator 개념 설명:
- 기존에 만든 함수에 새로운 기능을 추가 하고 싶을 때, 1) 기존 함수에 코드를 추가로 작성하는 방법, 2) 기존 함수와 동일한 코드를 지닌 새로운 함수를 만들고 그 곳에 새 코드를 작성하기 이 두가지로 나눌 수 있다. 아마 후자로 작업하기에는 메모리 사용측면이나, 적힌 코드 분량 관리에 있어서 크고 작은 어려움을 겪을 것이다.
- 그렇다면 전자의 방법으로 작업을 어떻게 용이하게 할 수 있을까? 한 가지 고려사항으로, 기존함수에 새 코드를 작성하게 되면, 나중에 해당 추가 기능을 다시 없애고 싶을 때 도전과제로 다가올 것이다. 이를 일일이 손으로 다 지우면 많이 불편할 것이다. 따라서 스위치를 켰다 컸다 하듯이 새 함수기능을 추가했다가 지웠다 하는 역할을 하는 방법이 고안된 것인데, 바로 지금 소개된 @ 데코레이터이다.
- 위 코드의 예시처럼 @some_decorator 를 def 시작라인 윗줄에 써서 코드 라인을 한 줄 썼다 지웠다 하면서 원하는 기능을 넣었다 뺐다 할 수 있음. 예) @decorator가 있다가 없으면 위 wrap_func 기능이 실행되었다가 다시 없어진다.
- 특히나 이 '@' 기능은 인터넷에 기작성된 코드를 따와서 응용하는데 꼭 필요한 까닭에 장고나 플라스크 웹개발 시 굉장히 많이 쓰인다. 따라서 여러 코드를 살필때 저 @오퍼레이터가 어떤 기능을 하는지 자세히 이해하고 잘 활용할 줄 알아야 한다!
Author And Source
이 문제에 관하여(TIL(9) - Decorator), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@kyeongraekim/Python3-TIL210401저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)