일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 레이아웃
- Optional
- optional binding
- AlignmentGuide
- Linked List
- 좌표공간
- Test
- swiftUI
- vstack
- stack
- 각도
- SWIFT
- Double Linked List
- enum
- layout
- Hashing
- JavaScript
- hstack
- Optional Chaining
- Universal Hashing
- 자료구조
- nodejs
- 생각
- 시계방향
- Today
- Total
klioop for iOS
decorator 이해하기 본문
파이썬 중급자로 도약하기 위해 class descriptor 를 이해해 보려고 했으나, 하루를 온전히 날리고 처참히 실패했다.
매우 어렵다.
파이썬은 배우면 배울수록 어려운 것 같다. 처음 배울 때가 제일 쉽게 느껴졌다 ㅜㅜ
그래도 어떻게든 중급자로 도약해야 하기 때문에 천천히 이해할 수 있는 것부터 시작해보고자 한다.
그래서 준비한 첫번째 주제는 데코레이터..
사실 처음에 간단한 예제만 보고 넘어갔는데, 파이썬 잘하려면 반드시 무조건 무조건 이해해야 하는 것이었다.
pycon 강의 몇 개 훑어보니까 제대로 이해하는 건 지금은 안되는 것 같고 (runtime, import time 관련해서 빠삭하게 이해해야 한다 ;;)기초적인 개념을 이해하고 활용 가능 한 경우부터 써먹어 보려고 한다.
우선, 데코레이터를 잘 이해하려면 First class function, Higher order functions, 그리고 Closure 개념을 이해해야 한다.
이것들은 파이썬 개념이라기 보다 프로그래밍 일반 지식으로 알아야 한다고 한다. 여기서는 자세히 다루지는 않는다.
익숙하지 않으면 유튜브에 검색해보면 좋은 설명이 많다.
간단하게 정리하면, First class function 은 함수는 다른 object 와 같아서 변수에 저장도 가능하고, 다른 함수의 argument 로도 쓰일 수 있는 등 일반 객체와 동등하게 취급된다는 의미이다.
higher order functions 는 함수 안에 또 다른 함수가 정의 되는 것을 의미한다.
Closure
Closure 는 한 함수 안에서 정의된 함수가 자신이 정의된 local scope 에서 선언된 다른 변수를 기억하고 그 변수에 접근 하는 것을 의미한다. 이 때, 바깥 함수의 실행이 끝난 후에도 그 변수를 기억하고 접근 가능한 것이 핵심이다.
Closure 를 이해하는 것은 처음에 쉽지 않을 수 있지만, 데코레이터를 잘 활용하기 위해서는 반드시 이해해야 하는 개념이다. 예시 하나를 살펴보자.
add_numbers 변수에 log(add) 함수를 저장하는 순간 outer function 인 log 함수는 add 함수를 인자로 받아 실행되고 inner_func 함수를 반환한다.
그러면 add 함수는 inner_func 으로 대체되고 inner_func 함수는 실행준비가 된다. 이 때, 이미 log 함수는 실행이 완료되었다.
그런데 add_numbers 함수를 실행시키면 inner_func 함수는 이미 실행이 완료된 함수 scope 에서 선언된 변수인 message 를 콘솔에 찍게된다.
이것이 Closure 이다. 이미 실행된 함수의 변수를 아직 실행되지 않은 안 쪽 함수가 기억하고 자신이 실행될 때 그 변수에 접근해서 사용한다.
Decorators
클로저의 개념을 알아봤다. 이제 데코레이터를 살펴보자. 먼저, 데코레이터를 잘 표현한 두 문장을 소개한다.
- Decorator is just a function that takes in anotehr function as an argument, adds some kind of functionality and then returns another function.
- A decorator adds some decorations to a function, returning a decorated function!
- 데코레이터는 함수에 데코레이션을 더하고 그 데코레이트 된 함수를 리턴하는 것이다.
- 의역하자면 이렇다. 데코레이터는 다른 함수를 인자로 받아서 어떤 기능을 추가한 다음 그 함수를 리턴하는 함수이다.
이 포스트를 다 읽고 나서 다시 저 두 문장을 봤을 때, 무슨 의미인지 더 잘 이해하기를 바란다(나 자신에게 하는 말..).
다음 코드를 살펴보자.
figure 2 에서 두 표현은 같은 의미이다.
어떤 함수를 데코레이트 해주는 순간 데코레이터 함수는 데코레이트 되는 함수를 인자로 받아 실행된다.
위 코드의 경우, @decorator_func 이 들어간 순간 파이썬 인터프리터는 decorator_func 을 실행시킨다.
그래서 display_info = decorator_func(display_info) 와 같은 의미인 것이다.
흐름을 살펴보면, display_info 가 데코레이트 되면, 1) decorator_func 함수는 실행된다.
그리고 2) 안에서 정의된 wrapper 함수를 실행시킨다.
그러면 3) display_info 함수는 wrapper 함수로 대체되고 wrapper 함수가 실행 대기 상태가 된다.
figure 1 마지막 줄처럼 4) display_info 가 호출되면 wrapper 함수가 실행되고 result 변수에 저장된 display_info 함수의 실행결과를 반환한다.
데코레이터를 사용할 때, 이 흐름을 잘 기억해야 한다. 그래야 두 개 이상의 데코레이터를 함께 사용하거나 인자를 받는 데코레이터를 사용할 때 흐름을 놓치지 않을 수 있다.
데코레이터는 함수의 실행 속도를 측정할 때도 사용한다. 다음 코드를 보자.
흐름은 이전 데코레이터 함수와 같다.
stack of decorators
두 개의 데코레이터를 동시에 사용할 수 있을까? 해보자
뭔가 이상하다. 데코레이터의 순서를 바꿔서 다시 해보자.
원하는 결과와는 다르게 출력되는 것을 알 수 있다.
나는 display_info 앞뒤로 before, after 문장이 출력되고 display_info 함수가 실행될 때 얼마나 걸리는 지 알고 싶은데, wrapper 가 결과에 끼어드는 것을 알 수 있다.
위에서 데코레이트 된 함수가 실행되는 흐름을 이해했다면 이 결과는 당연한 것처럼 보일 것이다.
tiem_deco, decorator_func 의 데코레이트 된 함수 실행순서 흐름을 살펴보자.
먼저 display_info 는 wrapper 함수로 대체되고 display_info 가 호출된 순간 wrapper 함수가 실행된다.
따라서 @decorator_func 가 데코레이트 해주는 함수는 display_info 가 아니라 wrapper 함수인 것이다.
그래서 orig_func.__name__ 이 wrapper 가 된다.
time_deco 만 적용된 display_info 함수를 찍어보면 이 함수가 wrapper 함수로 대체된 것을 확실히 알 수 있다.
자, 그러면 데코레이터를 두 개 쓰려면 어떻게 해야 할까?
데코레이터를 적용해도 원래 함수가 wrapper 로 변하지 않게 해주면 될 것 같다. 이렇게 하는 방법만 소개하고 그 원리는 소개하지 않겠다. 왜냐면 나도 아직 모르기 때문!!😱
from functools import wraps
위 헤더에서 처럼 파이썬에서 functools 모듈에서 wraps 를 import 해온다. 그 다음 데코레이터 함수의 wrapper 함수들을 wraps 로 데코레이터 해주면 된다. 이 때, wraps 의 인자로 orig_func 을 쓰는 것에 주의하자.
처음에 의도했던 대로 결과가 출력되는 것을 알 수 있다.
'python' 카테고리의 다른 글
변수와 메모리 (0) | 2021.03.16 |
---|---|
instance 의 attribute 를 찾는 순서 (0) | 2021.03.16 |
Variable-length Arguments with *args and **kwargs in Python (0) | 2021.03.16 |
globals(); python 에서 string 을 변수로 변환하는 방법 (0) | 2021.03.16 |