이번에 살펴볼 것은 파이썬의 고급 개념들이다.
클로저와 데코레이터
클로저 closure는 함수 안에 내부 함수를 inner function을 구현하고 그 내부 함수를 리턴하는 함수를 말한다. 이 때 외부 함수는 자신이 가진 변숫값 등을 내부 함수에 전달할 수 있다.
class Mul:
def __init__(self,m):
self.m = m
def __call__(self,n):
return self.m * n
if __name__== "__main__":
mul3 = Mul(3)
mul5 = Mul(5)
print(mul3(10)) // 30
print(mul5(10)) // 50
Mul이라는 클래스를 만들고 m 값을 하나 할당해준다.
__call__이라는 메서드는 클래스로 만든 객체에서 인수를 전달하여 바로 호출되는 함수이다.
즉, mul3(10)은 m이 3으로 정해진 객체에서 10이라는 인수를 받아 __call__에 있는 함수를 실행하게 된다. 이 때 10은 n에 할당되어 최종적으로 3 곱하기 10인 30이 출력되게 된다.
mul5는 m이 5인 객체이므로 5 곱하기 10을 하게 된다.
def mul(m):
def wrapper(n):
return m * n
return wrapper
if __name__ == "__main__":
mul3= mul(3)
print(mul3(10)) // 30
하나씩 코드를 따라가보자.
mul(3)이 실행되면 m에 3이 들어가게 된다. 그러면 mul 안에 있는 wrapper라는 함수는 3 * n 의 결과를 출력하게 될 것이다. 이런 값을 기능을 가진 함수를 리턴하는 것이 mul의 리턴값이다. 이 때 주의해야하는 것은 인수로 받은 m의 값이 wrapper 함수에 저장되어 리턴된다는 것이다.
그래서 mul3(10)을 하게 되면 wrapper(10)을 하는 것과 똑같은데 이 wrapper는 mul3를 선언할 때 3이 저장되어 들어왔으므로 30의 값을 리턴하게 된다.
이게 클로저다. 함수가 내부에서 선언된 함수를 리턴하고 그 내부 함수는 외부 껍데기에서 받아온 매개변숫값을 전달 받을 수 있다.
import time
def elapsed(original_func):
def wrapper():
start = time.time()
result = original_func()
end = time.time()
print('함수 수행시간 : {} 초'.format(end-start))
return result
return wrapper
def myfunc():
print("함수가 실행됩니다.")
decorated_myfunc = elapsed(myfunc)
decorated_myfunc()
// 함수가 실행됩니다.
// 함수 수행시간 : 0.00머시기 초
먼저 위의 함수는 함수의 실행 시간을 측정하기 위한 함수이다.
또 하나씩 살펴보자.
elapsed는 함수를 인자로 받는다.
그 안에 wrapper라는 내장함수는 start 시간을 측정하고 인자로 받은 함수를 실행항 후 결과값을 result에 담는다.
그 다음 end 시간을 측정하고 그 시간차를 print하고 result를 리턴하는 함수이다.
elapsed는 위의 기능을 가진 wrapper 함수를 리턴한다.
임의의 함수 myfunc를 하나 불러온 다음 decorated_myfunc를 선언해주었다.
decorated_myfunc는 myfunc 함수를 인자로 받은 elapsed 함수이다.
그리고 함수를 실행하게 되면
1. wrapper 함수 안에서 original_func 대신 myfunc가 들어가서
2. start랑 end 사이에서 실행 -> 이 때 "함수가 실행됩니다." 출력
3. 그리고 시간차를 나타내는 print 실행 -> 이 때 "함수 수행시간 : ~ 초" 출력
4. return 값은 따로 없으므로 그대로 함수 종료
클로저를 이용하여 기존 함수에 기능을 덧붙인 것이다. 기존 함수를 바꾸지 않고 기능을 추가할 수 있게 만드는 elapsed 같은 함수를 데코레이터 decorator 라고 한다. 말 그래도 함수를 꾸미는 함수이다.
위의 코드를 @문자를 사용해서 작성할 수도 있다.
import time
def elapsed(original_func):
def wrapper():
start = time.time()
result = original_func()
end = time.time()
print('함수 수행시간 : {} 초'.format(end-start))
return result
return wrapper
@elapsed
def myfunc():
print("함수가 실행됩니다.")
myfunc()
// 함수가 실행됩니다.
// 함수 수행시간 : 0.00머시기 초
@+함수명을 사용하면 데코레이터 함수로 인식하여 elapsed 안에 myfunc가 들어간 형태로 인식한 후 실행한다. 결과는 똑같다.
myfunc가 매개변수를 필요로 하면 어떨까?
import time
def elapsed(original_func):
def wrapper(*args, **kwargs):
start = time.time()
result = original_func(*args, **kwargs)
end = time.time()
print('함수 수행시간 : {} 초'.format(end-start))
return result
return wrapper
@elapsed
def myfunc(msg):
print("{} 출력되었습니다.".format(msg))
myfunc("You need Python")
// You need Python 출력되었습니다.
// 함수 수행시간 : 0.00머시기 초
예전에 배웠던 *args, **kwargs를 활용하면 된다.
*args는 모든 인수를 묶어서 튜플로 변환해주고, **kwargs는 "키=값" 형태의 인수를 딕셔너리로 변환해준다.
여기서는 "You need Python" 문자열이 *args로 전달되어 실행된 것이다.
'Python' 카테고리의 다른 글
파이썬 스터디 6장 - 파이썬 프로그래밍, 어떻게 시작해야 할까? (0) | 2023.07.06 |
---|---|
파이썬 스터디 5장 - 파이썬에 날개 달기 (2) (0) | 2023.07.05 |
Python 스터디 5장 - 파이썬 날개 달기 (1) (0) | 2023.06.30 |
Python 스터디 4장 - 파이썬의 입출력 (0) | 2023.06.27 |
Python 스터디 3장 - 프로그램의 구조를 쌓는다! 제어문 (0) | 2023.06.26 |