함수를 중첩하여 사용하는 방법이다. 중첩 함수, 혹은 내부 함수라고 불린다
def parent_function() :
def child_function() :
print("this is child function!")
child_function()
parent_function() # 출력 : this is child function!
중첩 함수는 부모 함수안에서만 호출이 가능하다. 즉 위 코드에서 child_function()은 부모 함수인 parent_function() 내부에서만 호출이 가능한 것이다.
중첩함수를 사용하는 이유
1. 불필요한 코드의 반복을 줄여 가독성을 높일 수 있다.
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")
print_all_elements([1,2])
'''
출력
1
2
'''
2. Closure
중첩 함수를 통해 부모 함수에 대한 변수나 정보를 가두어 사용하는 것을 의미한다. 처음 들었을때 Java의 encapsulation과 비슷한 의미인것 같지만 stack overflow에 검색해보니 다른 점이 있다고 한다.
간단하게 정리하자면
예를 들어 2의 n승을 구하는 함수를 정의해보자
def get_power_of_two(power) :
return 2 ** power
answer= get_power_of_two(8)
print(answer) # 출력 : 256
간단하다, 하지만 2의 n승만이 아닌, 다양한 숫자의 n승을 구하고 싶은 함수를 closure를 사용해 만들어보자
def generate_power(base_num) :
def nth_power(power) :
return base_num ** power
return nth_power
get_power_of_three = generate_power(3)
answer = get_power_of_three(3)
print(answer) # 출력 : 27
하나씩 분석해보자
ex) 3의 n승을 구하고 싶으면 base_num = 3으로 넘겨주면 된다.
generate_power(3)
get_power_of_three = generate_power(3)
여기서 return되는것은 중첩 함수인 nth_power이다. 중요한것은, return되는 nth_power 함수에서 base_num = 3으로 세팅되어 있는 상태로 get_power_of_three 변수에 return이 된다.
# return된 nth_power 함수
nth_power(power) :
return 3 ** power
get_power_of_three(power) :
return 3 ** power
아까 2의 n승을 구하는 함수랑 똑같아졌다.
마지막으로 answer라는 변수를 만들고 3의 3승을 구해보자
answer = get_power_of_three(3) print(answer) # 출력 : 27
closure의 원리를 이용해 파이썬의 Decorator를 구현해보자.
우선 Decorator의 기본 형태이다
@is_paid_user
def jackpot_stock_information() :
print("삼성전자를 사세요!!")
jackpot_stock_information() 함수위에 @is_paid_user가 파이썬의 decorator이다.
이 함수는 jackpot_stock_information() 함수가 실행되기전에 실행이 된다.
만일 주식 정보를 전달하는 jack_pot_stock_information() 함수가 회원에게만 전송이 되어야 한다면, 이 함수를 실행하기 전에 회원에 대한 인증을 진행해야 한다.
아래와 같이 조건문을 사용해서 처리할 수 있지만,
def is_paid_user :
if user_paid == True :
return True
else :
return False
if is_paid_user :
jackpot_stock_information()
else :
print("회원이 아닙니다.")
매번 이렇게 if문을 사용하다간, 내가 실수할 위험도 높아질 수 있다. 해당 함수가 실행되기 전에 decorator 함수가 먼저 실행되는 decorator의 특징을 활용하여 구현을 하는게 더 좋다.
우선 decorator도 결국에는 먼저 실행되는 함수가 무언가를 return 해줘야 결과에 따라 다음 함수가 실행되기 마련이다. 여기서 return 하는 무언가는 중첩 함수(nested function)이다.
위에 is_paid_user() 함수를 decorator로 만들어 보자.
def is_paid_user(func):
user_paid = True # 간단화 하기 위해 무조건 True
def wrapper(*args, **kwargs):
if user_paid:
func()
else:
return
return wrapper
사용자의 회원가입 여부를 담은 user_paid 변수는 간단하게 모두 True로 해놓는다.
그리고 위에서 만든 decorator를 사용해보자
@is_paid_user
def jackpot_stock_information() :
print("삼성전자를 사세요!!")
is_paid_user(func)에서 넘어가는 func 매개변수는 이 함수 다음에 실행되는 jackpot_stock_information() 함수이다.
이런식으로 decorator를 구현할 수 있다. 하지만 아직 익숙하지 않으니 좀 더 만들어봐야겠다.
파이썬의 scope는 4가지가 있다.
주로 함수 안에서 선언된 변수나 함수를 의미한다. 해당 함수 안에서만 유효하다.
def local_scope() :
num = 10
print(num) # 출력: 10
print(num) # 에러
부모 함수에서 선언한 변수나 함수는 중첩 함수에도 유효한 범위를 가진다.
def parent_function() :
name = "진성준"
def child_function()
print(name) # 출력 : "진성준"
함수 밖에서 선언된 변수나 함수를 의미한다.
name = "진성준"
def print_name() :
print(name) # 출력 : 진성준
파이썬안에 내장되어 있는, 파이썬이 제공하는 함수 또는 속성들이 built-in scope를 가지고 있다. 따로 선언이 필요없다.
파이썬이 변수나, 함수의 정의를 찾을 때의 순서이다
local -> enclosing -> global -> built-in
가장 좁은 scope부터 넓은 scope까지 순차적으로 찾는다. 이런 특성때매 다음과 같은 오류가 생길 수 있다.
a = 9
def print_num() :
a = 1
print(a)
print_num() # 출력 : 1
print(a) # 출력 : 9
똑같은 a라는 변수이지만, print_num() 함수를 벗어난 범위에서 a를 출력하면 9가 나온다. 이렇게 더 큰 scope내의 변수나 함수가 더 작은 범위의 변수나 함수를 가리는것을 shadowing이라고 한다.