티스토리 뷰
📌 데코레이터
이미 정의되어 있는 함수에 기능을 추가하고자 한다면 데코레이터를 사용한다. 가변 인자를 반드시 사용해야 하는 것은 아니나, 여러 가지 상황에 대응할 수 있으므로 추천한다.
📍 어떤 함수의 이름을 인자로 받아 꾸며준 후, 다시 해당 함수를 리턴(실행)한다.
📍 데코레이트 할 함수의 함수의 정의부 상단에 @데코레이터명을 추가하는 방법으로 사용할 수 있다.
📍 이미 정의된 함수에 로직을 추가하므로 코드 중복을 최소화하고 재사용성이 향상된다.
📍 데코레이터는 중첩이 가능하다. 하지만 가독성이 떨어지고 디버깅이 어렵다는 단점이 있다.
📍 매개 변수로 *args와 **kwargs를 사용하는 것을 추천한다.
👉 데코레이터 만들기
def hello() :
print('hello')
def hi() :
print('hi')
✔ 출력문을 하나씩 가지는 함수에 함수의 시작과 끝을 출력하는 기능을 데코레이터를 사용해 추가해보자.
def deco(func) :
✔ 데코레이터 함수는 기능을 확장할 함수를 매개 변수로 갖는다.
def deco(func) :
def deco_func() : #기능이 추가된 함수
print('%s()함수 start' %func.__name__)
func() #기능 확장을 위해 데코함수 내부에 반드시 기존 함수 실행
print('%s()함수 end' %func.__name__)
return deco_func #기능이 추가된 함수를 반환
✔ 데코레이터 함수 내부에서 기능이 확장된 함수 deco_func()를 새롭게 생성한다.
✔ 기능이 확장된 함수 내부에서는 매개 변수로 가져온 함수 func()가 실행되어야 한다.
✔ 그러면 hello만 출력할 수 있었던 함수 hello()에 함수의 시작과 끝을 출력할 수 있게끔 기능이 확장된다.
✔ 마지막으로 기능이 확장된 함수 deco_func를 반환한다.
deco_hello = deco(hello)
deco_hi = deco(hi)
deco_hello()
deco_hi()
✔ 반환된 deco_func를 새로운 변수를 사용해 참조하여 사용할 수 있다.
✔ 전체 코드는 아래와 같다
def deco(func) :
def deco_func() : #기능이 추가된 함수
print('%s()함수 start' %func.__name__)
func() #기능 확장을 위해 데코함수 내부에 반드시 기존 함수 실행
print('%s()함수 end\n' %func.__name__)
return deco_func #기능이 추가된 함수를 반환
def hello() :
print('hello')
def hi() :
print('hi')
deco_hello = deco(hello)
deco_hi = deco(hi)
deco_hello()
deco_hi()
👉 @를 사용해 데코레이터 만들기
def deco(func) :
def deco_func() : #기능이 추가된 함수
print('%s()함수 start' %func.__name__)
func() #기능 확장을 위해 데코함수 내부에 반드시 기존 함수 실행
print('%s()함수 end\n' %func.__name__)
return deco_func #기능이 추가된 함수를 반환
def hello() :
print('hello')
def hi() :
print('hi')
deco_hello = deco(hello)
deco_hi = deco(hi)
deco_hello()
deco_hi()
▼
def deco(func) :
def deco_func() : #기능이 추가된 함수
print('%s()함수 start' %func.__name__)
func() #기능 확장을 위해 데코함수 내부에 반드시 기존 함수 실행
print('%s()함수 end\n' %func.__name__)
return deco_func #기능이 추가된 함수를 반환
@deco
def hello() :
print('hello')
@deco
def hi() :
print('hi')
hello()
hi()
✔ 기능을 확장하고자 하는 함수 정의 위에 @데코레이터 함수명을 추가한다.
✔ 반환받은 함수를 새로운 변수를 사용해 참조하지 않고 기존 함수를 호출하면 확장된 기능이 실행된다.
👉 매개변수와 반환 값이 있는 함수의 데코레이터
@deco
def add(a,b) :
return a + b
✔ 매개변수와 반환값이 있는 함수의 데코레이터를 만들어보자.
def deco(func) :
def deco_func(a,b) :
res = func(a,b)
print('a + b = ',res)
return res
return deco_func
✔ 기존 함수에 매개 변수와 반환 값이 없었을 때, deco_func 함수에도 매개 변수와 반환 값을 사용하지 않았다.
✔ 기존 함수에 매개 변수와 반환값이 있다면, deco_func 함수에도 동일하게 사용해야 한다.
✔ 만약 기존 함수의 매개변수가 가변 인자라면 동일하게 사용해야 한다.
✔ 이 때, 위치 인수와 키워드 인수를 모두 받을 수 있도록 *args와 **kwargs를 지정한다.
def deco(func) :
def deco_func(a,b) :
res = func(a,b)
print('a + b = ',res)
return res
return deco_func
@deco
def add(a,b) :
return a + b
add(10,20)
✔ 전체 코드와 실행 결과는 위와 같다.
👉 매개변수가 있는 데코레이터
def html_tag(tag) :
def wrapper(func) :
def deco_func() :
start_tag = '<'+tag+'>'
end_tag = '</'+tag+'>'
return start_tag+func()+end_tag
return deco_func
return wrapper
a, b = input().split()
@html_tag(a)
@html_tag(b)
def hello():
return 'Hello, world!'
print(hello())
✔ 먼저 매개변수를 받는 함수로 감싸주고, 일반적으로 사용하는 데코레이터를 중첩 함수로 정의한다.
✔ 중첩 함수만큼 return값이 필요하니 주의하자.
👉 여러 데코레이터를 붙이기
def decoA(func) :
def deco_func(a,b) :
func(a,b)
print('a + b = ',a+b)
print('a - b = ',a-b)
return deco_func
def decoB(func) :
def deco_func(a,b) :
func(a,b)
print('a * b = ',a*b)
print('a / b = ',a/b)
return deco_func
@decoA
@decoB
def num(a,b) :
print('{0}와 {1}의 사칙연산'.format(a,b))
num(20,4)
✔ 여러 데코레이터를 지정하면, 위에서 아래로 데코레이터가 실행된다.
✔ 여러 데코레이터를 사용한다는 건 기존의 함수에 기능을 2가지 추가한 것이므로 기존 함수는 1번만 실행된다.
deco_num = decoA(decoB(num))
deco_num()
✔ @를 사용하지 않으면 위의 코드와 동일하게 동작한다.
👉 클래스로 데코레이터 구현하기
@Deco
def hello() :
print(hello)
✔ 클래스로 데코레이터를 만들 때에는 인스턴스 함수처럼 호출하게 해주는 특수 메서드 __call__()를 구현해야 한다.
✔ 클래스로 데코레이터를 만들 때에도 매개변수의 개수, 형태, 반환 값의 유무를 기존 함수와 동일하게 사용해야 한다.
class Deco :
def __init__(self, f) :
self.func = f
✔ 클래스의 인스턴스 변수로 기존 함수명을 넘겨주어야 하기 때문에 생성자를 정의한다.
class Deco :
def __init__(self, f) :
self.func = f
def __call__(self) :
print(__name__,' start')
self.func()
print(__name__,' end')
✔ 특수 메서드 __call__을 구현할 때에 생성자를 통해 인스턴스 변수로 만들어둔 기존 함수를 반드시 사용하자.
✔ 클래스를 사용해 데코레이터를 구현할 때는 메서드에 반환 값이 있을 수는 있지만
✔ __call__()는 객체를 함수 호출 방법으로 호출하므로, 기능이 추가된 함수명을 반환할 필요는 없다.
class Deco :
def __init__(self, f) :
self.func = f
def __call__(self) :
print(__name__,' start')
self.func()
print(__name__,' end')
@Deco
def hello() :
print('hello')
#객체를 생성하고, 객체를 호출해 확장된 기능의 hello()함수 실행
test = Deco(hello)
test()
#기존 함수를 호출해도 확장된 기능의 hello()함수가 실행
hello()
✔ __call__()를 사용한 덕에 기존 함수를 호출하더라도 Deco 객체를 호출하듯 사용할 수 있다.
[참고] 엘리스 트랙-혼자 공부하는 파이썬 / 길벗-파이썬 코딩 도장 / 엘리스 트랙 - 예제 중심의 파이썬 입문 / 위키백과
'BackEnd > Python' 카테고리의 다른 글
[파이썬 개발 도구] 기본 개발 툴(IDLE) / 주피터 노트북 (0) | 2021.03.29 |
---|---|
[파이썬 기초] 06-1. 이터레이터와 제너레이터 (0) | 2021.03.24 |
[파이썬 기초] 05-3. 상속과 추상 클래스 (0) | 2021.03.23 |
[파이썬 기초] 05-2. 특수 메서드 (매직 메서드) (0) | 2021.03.23 |
[파이썬 기초] 05-1. 객체와 클래스 (0) | 2021.03.23 |