다른 구문들과 마찬가지로 함수도 함수안에 중첩되어 선언될 수 있다.
즉, 함수안에 함수 선언이 가능하다
## Nested Function
def parent_function(): ## 부모함수
def child_function(): ## 중첩함수
print("this is a child function")
child_function() ## 중첩함수 호출
parent_function() ## 부모함수 호출
# >> "this is a child function"
중첩함수(Nested Function) 혹은 내부 함수는 상위 부모 함수안에서만 호출이 가능하다
부모함수를 벗어나 호출할 수 없다.
위 예시에서 child_function(Nested Function)함수는 paraen_function(부모함수)안에서만 호출이 가능하다.
중첩함수(Nested Function)은 왜 쓰는 것일까?
함수를 사용하는 이유중 하나는 반복되는 코드블록을 함수 하나로 정의해서 효과적으로 코드를 관리하는 이유다.
마찬가지로 중첩함수도 함수 안에 반복되는 코드가 있다면 중첩함수로 선언하여 부모함수의 코드를 효과적으로 관리하고 가독성을 높일 수 있다.
def print_all_elements(list_of_things): ## 부모함수 선언
def print_each_element(things): ## 중첩함수 선언
for thing in things:
print(thing)
if len(list_of_things) > 0:
print_each_element(list_of_things)
else:
print("There is nothing!")
중첩함수를 사용하는 다른 이유는 Closure이다.
Closure란, 사전적의미로는 '폐쇄'라는 뜻을 가진다.
파이썬에서 사용하는 Closure는 어떤 것을 외부로 부터 격리하여 사용한다는 느낌이 있다.
즉, 중첩함수의 부모함수가 자신의 내부에 함수나 변수의 정보를 가두어 사용하는 것을 Closure라고 하는 것이다.
그리고 부모함수는 중첩 함수를 리턴해준다. 그리하면 부모함수의 변수를 외부로부터 직접적인 접근은 격리하면서도 중첩 함수를 통해서 격리된 부모함수의 변수를 사용한 연산은 가능하게 해주는 것이다
만일 어떠한 숫자의 수(數)를 구하는 함수가 있다면...
def calculate_power(number, power):
return number ** power
calculate_power(2, 7)
> 128
주어진 2,7 이 아닌,
이제 2의 승을 구하는 함수를 구한다면 다음과 같다.
def calculate_power_of_two(power):
return 2 ** power
calculate_power_of_two(7)
> 128
calculate_power_of_two(10)
> 1024
하지만 위의 함수는 2의 승밖에 구할수 없다.
따라서 만일 특정 숫자의 승을 구하는데, 승도 따로 설정해주는 수를 구하는 함수가 있다면 다음과 같다
def generate_power(base_number): ## 부모함수 선언
def nth_power(power): ## 중첩함수 선언
return base_number ** power ## 리턴
return nth_power ## 부모함수에서 중첩함수 리턴
calculate_power_of_two = generate_power(2) ## 부모함수에 인자값 넣고 변수에 넣음 2 => base_number
calculate_power_of_two(7) ## 부모함수가 담긴 변수에 중첩함수 인자값을 넣음 7=>power
> 128
calculate_power_of_two(10)
> 1024
calculate_power_of_seven = generate_power(7)
calculate_power_of_seven(3)
> 343
calculate_power_of_seven(5)
> 16907
Decorator는 사전적의미로 '장식 혹은 장식하는 사람'이라는 뜻이다. 즉, 뭔가 장식에 관련된거라는 것을 짐작할 수 있다.
파이썬에서 Decorator는 중첩함수를 사용하고 그 함수들을 호출할 때 사용한다.
예를들어 중첩함수 구조를 만들고 호출하고자 할때...
## Decorator example#1
def is_paid_user(func):
user_paid = True # 간단화 하기 위해 무조건 True
def jackpot_stock_information():
return "계시가 내려졌습니다. 삼성전자를
if is_paid_user():
jackpot_stock_information()
만약 jackpot_stock_information 함수가 여러 곳에서 자주 사용된다면, jackpot_stock_information 함수가 호출 될때 is_paid_user가 무조건 먼저 호출 되어야 하는데, 이 연결고리를 잊어먹을 확률이 있다.
따라서 두 함수의 연결고리를 강제적으로 묶어줄 필요가 있다.
## Decorator example#2
def is_paid_user(func):
user_paid = True # 간단화 하기 위해 무조건 True
def wrapper(*args, **kwargs):
if user_paid:
func()
else:
return
return wrapper
@is_paid_user
def jackpot_stock_information():
return "계시가 내려졌습니다. 삼성전자를 사세요!"
jackpot_stock_information 함수 정의 위에 is_paid_user 함수가 골뱅이 마크 (@) 와 함께 달려있는것을 볼 수 있다. 그래서 장식, 즉 decorator 라고 하는것이다.
Decorator는 중첩함수(Nested Funcion)을 return하는 함수만 decorator 함수를 사용할 수 있다.
그 이유는 Decorator의 기능을 다르게 설명 하면 chain of functions, 즉 여러개의 함수가 연속적으로 호출이 자동으로 되게 해주기 때문이다.
Decorator를 구현해보세요.
Decorator는 앞서 배운 closure를 한단계 더 나아가 사용하는 고급 기능입니다.
Decorator는 closure처럼 중첩함수를 리턴하는 함수 이며 다른 함수에 적용해서, 적용된 함수가 실행되기 전에 무조건 실행됩니다. 즉 특정 함수를 실행하기 전에 강제적으로 다른 함수가 먼저 실행된후 실행되도록 하는 강제성을 제공하는 기능입니다.
더 자세한 내용은 아래 링크의 스택오버플로우 질문을 참조하세요:
https://stackoverflow.com/c/wecode/q/64/1
왼쪽 상단의 greeting 함수에 적용될 decorator 함수를 구현하여 greeting 함수에 적용해주세요.
Greeting 함수가 호출 되었을때 decorator 함수에 parametor 값이 greeting 함수 리턴값의 다음에 붙혀져서 리턴되어야 합니다. Decorator 함수의 이름은 name_decorator 여야 합니다.
예를 들어, 다음 처럼 정의 하여 name_decorator decorator에 "정우성"을 parameter로 적용하여 greeting을 호출하면:
@name_decorator("정우성")
def greeting():
return "Hello, "
greeting()
결과값은 다음과 같아야 합니다.
"Hello, 정우성"
## My Solution
def name_decorator(name): ## 부모함수 선언
def real_deco(func): ##
def wrapper():
r = func() + name
return r
return wrapper
return real_deco
@name_decorator("정우성")
def greeting():
return "Hello, "
greeting()
## Model Solution
def name_decorator(name):
def _decorator(func):
def wrapper():
result = func()
return result + name
return wrapper
return _decorator
def greeting():
return "Hello, "