Decorate
처음 논문을 구현한 코드 등을 보거나 깃허브를 둘러보면 이 Decorate 때문에 당황한 경험이 있을 것이다
쉽게 말 그대로 코드를 꾸밀 수 있는 기능으로 생각하면 편하다 (@를 사용한다)
아래 예시로 쉽게 파악이 가능하다
def Deco(func):
print("#" * 14)
print("## 함수를 수행합니다.")
func()
print("#" * 14)
@Deco
def sum_one2five():
sum = 0
for i in range (1,6):
print("## ",i,"번째 처리중")
sum += i
print("## 결과값 : ", sum)
print("\n")
@Deco
def mul_one2five():
mul = 1
for i in range (1,6):
print("## ",i,"번째 처리중")
mul *= i
print("## 결과값 : ", mul)
sum_one2five()
다음을 실행하면 아래와 같은 결과를 얻을 수 있다
##############
## 함수를 수행합니다.
## 1 번째 처리중
## 2 번째 처리중
## 3 번째 처리중
## 4 번째 처리중
## 5 번째 처리중
## 결과값 : 15
##############
##############
## 함수를 수행합니다.
## 1 번째 처리중
## 2 번째 처리중
## 3 번째 처리중
## 4 번째 처리중
## 5 번째 처리중
## 결과값 : 120
##############
TypeError: 'NoneType' object is not callable
대충 어떤 역할을 하는지는 알겠는데
왜 일반적으로 함수를 호출하게되면
TypeError: 'NoneType' object is not callable 라는 오류가 나오는지
그러면 어떻게 사용하는지에 대한 감이 안잡히게 된다
Decorator에 대해서 알아보자
다음 단계로 설명이 가능하다
- First class objects (1급 객체)
- inner function
- Decorator
First class objects
파이썬에서 말하는 First class objects 즉, 일급객체란
쉽게 변수나 데이터 구조에 할당이 가능한 객체를 뜻하는 말로
파라미터나 리턴값으로 사용이 가능한 객체를 말한다
추가로 파이썬의 모든 함수는 일급함수이다 (함수 이름을 변수처럼 사용이 가능)
우리가 자주 사용하는 map 함수
def func_twice(x):
return x **2 # x 의 제곱을 반환
result = list(map(func_twice, [1, 2, 3, 4, 5]))
result
>>> [1, 4, 9, 16, 25]
다음과 같이 func_twice 라는 함수를 변수처럼 parameter로 주고 받는 것이 가능한 것을 볼 수 있다
이러한 성격을 일급객체/ 일급함수라고 한다
Inner function
python에서 말하는 inner function 즉, 내제 함수란
함수 내에 또 다른 함수가 존재하는 구조이다
아래와 같은 예시를 보자
def print_msg(msg):
def printer():
print(msg)
printer()
print_msg("Hello world")
다음 코드를 설명해 보면
Hello world 가 msg 변수에 담겨서 print_msg에 들어가게 되고
이를 통해 printer라는 함수가 선언되며
print_msg 는 printer를 실행시킨다
주로 사용하는 이유가 자바스크립트에서
Closures 이라는 것으로 활용이 된다
(Closures : inner function을 return 값으로 반환)
def student(name, num, year):
name = name
num = num
def inner_func():
student_number = year*10000 + num
return 'This student name : %s and student_number : %d' %(name, student_number)
return inner_func
s1 = student('홍길동', 13, 2017)
s2 = student('새내기', 76, 2023)
>>> This student name : 홍길동 and student_number : 20170013
>>> This student name : 새내기 and student_number : 20230076
즉, 여기서 s1 은 다음과 같다
def s1():
return 'This student name : 홍길동 and student_number : 20170013'
그러면 이 Closer랑 Decorator랑 무슨 관련이 있을까???
Decorator
Decorator는 복잡한 Closure 함수를 간단하게 만들어 준다고 생각하면 된다
(물론 Closure 함수 이니깐 inner function을 return 시켜준다)
위에 예시를 다시 살펴보자
def Deco(func):
print("#" * 14)
print("## 함수를 수행합니다.")
func()
print("#" * 14)
@Deco
def sum_one2five():
sum = 0
for i in range (1,6):
print("## ",i,"번째 처리중")
sum += i
print("## 결과값 : ", sum)
sum_one2five()
Deco 라는 Decorator는 일종의 Closure 함수라고 생각할 수 있는데
inner function을 return 을 해준다
근데 여기는 inner function이 따로 정의가 되어 있지 않으므로
당연히 NoneType error 가 나오게 되는 것이다
또한 따로 정의하는 것이 아닌
함수 바로 안쪽에 print 실행 문이 있으므로
비록 sum_one2five() 만 실행했음에도 불구하고
mul_one2five()의 결과도 함께 출력이 되어 버렸다
위를 제대로 동작하게 하고 싶으면
def Deco(func):
def inner():
print("#" * 14)
print("## 함수를 수행합니다.")
func()
print("#" * 14)
return inner
@Deco
def sum_one2five():
sum = 0
for i in range (1,6):
print("## ",i,"번째 처리중")
sum += i
print("## 결과값 : ", sum)
print("\n")
@Deco
def mul_one2five():
mul = 1
for i in range (1,6):
print("## ",i,"번째 처리중")
mul *= i
print("## 결과값 : ", mul)
sum_one2five()
다음과 같이 inner function을 통해 감싸주면 된다
즉. Decorator는 inner function을 안에 넣어주고 return 시켜줘야 한다
만약 메세지를 다음과 같은 형식으로 출력해주는 코드를 만들고 싶다면
def Deco(func):
def inner(*arg, **kwargs):
print("#" * 14)
func(*arg, **kwargs)
print("#" * 14)
return inner
@Deco
def print_msg(msg):
print(msg)
print_msg("Hello this is python Decorator")
>>> ##############
>>> Hello this is python Decorator
>>> ##############
이런 식으로 활용가능하고 여러개도 다음과 같이 가능하다
def star(func):
def inner(*arg, **kwargs):
print("*" * 14)
func(*arg, **kwargs)
print("*" * 14)
return inner
def blank(func):
def inner(*arg, **kwargs):
print(" " * 14)
func(*arg, **kwargs)
print(" " * 14)
return inner
@star
@blank
def print_msg(msg):
print(msg)
print_msg("Hello this is python Decorator")
>>> **************
>>>
>>> Hello this is python Decorator
>>>
>>> **************
근데 위에 같이 복잡하게 매번 선언하는 것이 아닌
Decorator에 값을 넣어주고 싶으면
함수로 한번 더 감싸주면 된다 (주로 wrapper라고 쓴다)
def custom(shape):
def wrapper(func):
def inner(*arg, **kwargs):
print(shape * 14)
func(*arg, **kwargs)
print(shape * 14)
return inner
return wrapper
@custom("*")
@custom("#")
def print_msg(msg):
print(msg)
print_msg("Hello this is python Decorator")
'Python > Basic' 카테고리의 다른 글
[Python] Class 와 OOP에 관하여 (0) | 2023.04.05 |
---|---|
Python에서의 *args 와 **kwargs (0) | 2023.04.04 |
[AI for Python] 행렬의 곱셈 정리 (0) | 2022.10.04 |
[Python Basic] Python 에 수식표현(Sympy) (0) | 2022.10.03 |
[Python Basic]조건문과 반복문 (0) | 2022.09.23 |