def greeting():
def hello():
print("Hello!")
hello()
>>>greeting()
Hello!
위의 코드를 살펴보면 greeting이라는 함수 안에서 hello라는 함수를 호출 했기 때문에
gretting 함수만 Call을 해도 'Hello!'라는 문구가 정상적으로 출력 된다.
또한, 중첩함수(hello)는 부모함수(greeting) 안 에서만 사용할 수 있다.
만약 greeting 함수 밖에서 hello를 사용했다가는 가차 없이 에러가 뜰 것이다.
그렇다면 중첩 함수를 쓰는 이유는 무엇일까?
크게 두 가지의 이유가 있다.
- 가독성
- Closure
부모함수의 변수나 정보를 가두는것을 'closure'라고 한다.
Closure에는 세 가지 조건이 있다.
이러한 조건을 보았을 때, Closure는 어떤 정보를 기반으로 연산을 실행하고 싶으나 그 정보의
접근을 제한하여 노출이나 수정을 막고자 할 때 사용!
Closure는 주로 factory 패턴을 구현할 때 사용한다. factory는 무엇인가를 생성해내는 패턴이다. 주로 함수나 Object를 생성하는데 사용한다. factory에서 뭔가를 생성하기 위해서는 설정값이 필요하다. 결국, 설정값을 노출하지 않아서 수정이 불가능하게(closure의 개념)
하면서 해당 설정 값을 기반으로 한 연산을 할 수 있는 함수를 만들 때, closure를 사용할 수 있다.
다음과 같은 예제코드가 있다
def generate_power(base_number): def nth_power(power): return base_number ** power return nth_power #closure 생성 및 출력 >>calculate_power_of_two = generate_power(2) >>calculate_power_of_two(7) 128
위의 함수는 승(power) 또는 지수라고 하는 값을 구하는 함수이다.
생성 및 출력 부분을 보면, generate_power에 arguments 2를 넣은 값(즉 함수)을
calculate_power_of_two 라는 변수에 대입합니다.
Decorator의 역할은 '함수를 인자로 받아 새로운 함수를 만들어 변환하는 함수' 이다.
함수 실행 전 특정동작을 하게 하는걸 간단하게 할 수 있게 만드는 것이라고 할 수 있다.
아래의 예제 코드를 살펴보자
import datetime def delivery_ok(): print(datetime.datetime.now()) print("배송완료") #결과 >>delivery_ok() 2022-01-03 11:45:39.811267 배송완료
이런 함수가 여러개이거나 delivery_ok 라는 함수에 기능이 많을 수록 가독성은 떨어지고,
에러 찾기도 어려워진다.
이런 이유 때문에 조금 전 설명한 Decorator를 사용한다.
우선 중첩함수(function)을 만들어야 한다.
def decorator(func): def wrapper(): print(datetime.datetime.now()) return func() return wrapper def delivery_ok(): print("배숑완료") delivery_ok = decorator(delivery_ok) delivery_ok()
위와 같이 decorator의 뼈대 기능이 구현 되었다.
12번째 Line에서 Closure와 같이 decorator 함수가 실행된다.
decorator라는 함수는 함수를 인자로 받기 때문이다.
결과값은 다음과 같다.
2022-01-03 12:03:27.072163
배숑완료
*args, **kwargs 받기
이제 추가기능으로 배송지까지 나타내 보자.
import datetime def decorator(func): def wrapper(*args, **kwargs): print(datetime.datetime.now()) return func(**kwargs) return wrapper def delivery_ok(**kwargs): print("배송완료") if 'where' in kwargs: print(f"배송지는 {kwargs['where']} 입니다.") delivery_ok = decorator(delivery_ok) delivery_ok(where='대전')
decorator 함수도 이에 맞게 수정 해야 한다.
우선 함수를 받는다는건 동일하다.
wrapper는 Line15에서 함수가 실행될 때, 형태가 wrapper(where="송파") 와 같다.
따라서 wrapper 함수는 where라는 kwargs를 받고, 시간을 출력하고,
최종적으로 delivery_ok를 실행한다.
결과값은 다음과 같다.
2022-01-03 13:28:54.894273
배송완료
배송지는 대전 입니다.
본함수 = decorator_function(본함수)형태를 한 문장으로 표현하는 방식이 있다.
위의 코드 중 delivery_ok = decorator(delivery_ok)가
@decorator로 치환 가능하다.
바로 위에서 사용했던 코드에서 일반/등기의 구분이 너무 중요해서 함수에 넣는다고 가정한다.
import datetime def type_decorator(type): def decorator(func): def wrapper(*args, **kwargs): whereis = func(**kwargs) + "인" print(datetime.datetime.now()) print(f"{whereis} {type} 택배입니다.") return wrapper return decorator @type_decorator("등기") def delivery_ok(**kwargs): print("배송완료") if 'where' in kwargs: return "배송지는" + kwargs['where'] delivery_ok(where='송파' ,company="한진")
최종 출력
배송완료
2022-01-03 13:52:28.478306
배송지는송파인 등기 택배입니다.