TIL - 03/26 - Python - 1

Sung Jun Jin·2020년 3월 26일
0

TIL

목록 보기
5/25

Nested Function

함수를 중첩하여 사용하는 방법이다. 중첩 함수, 혹은 내부 함수라고 불린다

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에 검색해보니 다른 점이 있다고 한다.

간단하게 정리하자면

  • 중첩 함수가 부모 함수의 정보를 가두어 사용하는 것이 closure이다.
  • 부모 함수는 중첩 함수를 return 해준다.
  • 부모 함수에서 return 했으므로 부모 함수에 변수나 정보에 대한 직접적인 접근은 불가능.
  • 하지만 부모 함수의 변수를 사용한 연산은 return된 중첩 함수를 사용해 간접적으로 접근 가능

예를 들어 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

하나씩 분석해보자

  1. 우선 generate_power 함수는 base_num을 인자로 받는다. 여기서 base_num 인자는 어떤 숫자의 n승을 구할건지에 대한 인자이다.

ex) 3의 n승을 구하고 싶으면 base_num = 3으로 넘겨주면 된다.


generate_power(3)
  1. base_num의 인자로 3을 넘겨주게 되면 generate_power()의 return 문으로 인해 내부의 중첩 함수인 nth_power(power)가 get_power_of_three라는 변수에 넘어가게 된다.
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
  1. 중첩 함수를 return 받은 get_power_of_three 변수는 다음과 같다고 보면 된다.
get_power_of_three(power) :
    return 3 ** power

아까 2의 n승을 구하는 함수랑 똑같아졌다.

마지막으로 answer라는 변수를 만들고 3의 3승을 구해보자

answer = get_power_of_three(3) print(answer) # 출력 : 27

Decorator

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

파이썬의 scope는 4가지가 있다.

  • local scope

주로 함수 안에서 선언된 변수나 함수를 의미한다. 해당 함수 안에서만 유효하다.

def local_scope() :
    num = 10
    print(num) # 출력: 10

print(num) # 에러 
  • Enclosing Scope

부모 함수에서 선언한 변수나 함수는 중첩 함수에도 유효한 범위를 가진다.

def parent_function() :
    name = "진성준"
    
    def child_function()
        print(name) # 출력 : "진성준"
        
  • Global Scope

함수 밖에서 선언된 변수나 함수를 의미한다.

name = "진성준"

def print_name() :
    print(name) # 출력 : 진성준
  • Built-in Scope

파이썬안에 내장되어 있는, 파이썬이 제공하는 함수 또는 속성들이 built-in scope를 가지고 있다. 따로 선언이 필요없다.

Shadowing

파이썬이 변수나, 함수의 정의를 찾을 때의 순서이다

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이라고 한다.

profile
주니어 개발쟈🤦‍♂️

0개의 댓글