[wecode 6일차] - Decorator에 앞서

hyuckhoon.ko·2020년 5월 30일
0

What I learned in wecode

목록 보기
35/109

1. Decorator에 앞서

  • 사전 필요 개념
    • First-Class Functions
    • HOF (Higher-Order Functions)
    • Closure



[1] First-Class Functions

1) First-Class Functions란?

함수를 객체처럼 다루는 것
= 임의의 변수에 함수명을 할당(저장, assign)한 후, 객체처럼 사용.

새로운 컨셉을 배우고 있는 것이며 특히 어렵게 느껴질 수 있다.
왜냐하면 기존방식에 매우 익숙해져 있기 때문이다.

기존 방식 : 함수의 리턴값을 변수에 할당하는 것

    def add(a, b):
        return a+b
        
    # 함수의 리턴값 '7'을 변수 result에 할당(assign)
    result = add(2, 5)
    print(result)

(자바스크립트에도 존재하는 syntax이며, '컨셉'을 이해하는게 중요하다.)



아래 예시들을 통해 새로운 개념이 어떤 이점을 가져다 주는지 확인해보자.

2) 예시

    def func(a, b):
        return a+b
        
    # 함수명을 바꿔서 사용이 가능하다.
    add = func
    print(add(40, 60))

이 예제만 보면 함수명을 바꿀 수 있다는 것은 큰 장점이 없어 보인다.
애초에 함수명을 잘 지으면 되기 때문이다.

그래도 우선,
기존의 함수명 func를 add에 assign했고, (메모리 주소 복사)
func를 객체처럼 사용할 수 있구나정도만 이해하고 넘어가자.

[2] Higer-Order Functions

1) HOF란?

computer science에는 HOF라는 개념이 있다.
Higher-Order Functions는 아래의 조건 중 하나 이상을 만족해야 한다.

  • 함수가 다른 함수를 인자(argument)로 받거나,
  • 함수명을 리턴
    (ex) return add

    위의 두 조건이 왜 HOF(고차함수)라는 단어로 명명하게 된건지는
    조건을 자세히 보면 알 수 있다.
    함수를 인자로 받거나 리턴한다는 것은 결국 함수 안에 또 다른 함수가 존재한다는 것이다.
    즉, 자연스레 함수의 '계층구조'가 생긴다.

2) 예시

(1)

    def add(a, b):       # <---- 첫 번째 예제를 통해 배운 개념
        return a+b       
    func = add           # '함수명'을 변수에 할당

    def map_func(func):  # <---- HOF 개념 : (func)
        result = []      
        for i in range(5):
    	    result.append(func(i, i+1))
        return result
        
    # 함수 호출
    total = map_func(add)
    print(total)

추가로, 짚고 넘어가야할 내용이 있다.

(추가1) 함수 옆에 괄호가 있으면 그 함수를 실행시킨다는 의미다.

  • 따라서, print(add)와 같이 print(함수명)형태는
    그 함수가 저장된 메모리의 주소가 출력되고, 실행되진 않는다.

(추가2) 중첩함수 : 함수 안에 또 다른 함수가 정의되어 있는 함수가 있다. 그 안에 정의된 함수를 '중첩함수'(=내장함수)라고 한다.

  • A function defined inside another function is called a nested function

(2) 중요

아래 예제를 직접 따라해보면 이해에 큰 도움이 된다.

    def html_tag(tag): # <---- 부모함수
        
        def content_area(content):  # <---- 중첩함수(자식함수) 시작 부분
            print(f'<{tag}>{content}</{tag}>')
        # content_area 함수를 리턴하고 있다.(HOF 개념 사용!)   
        return content_area
	
    # 인자가 'h1'인 함수를 print_h1에 저장(할당)
    print_h1 = html_tag('h1')
    
    # 아래 코드의 이해가 중요
    print_h1('This is a h1 tag.')

    print_p = html_tag('p')
    print_p('This is a p tag')

위의 예제는 HOF의 개념과 First-Order Function 개념이 섞여 있다.

  • HOF 개념 : 함수를 리턴 또는 함수를 '인자'로써 전달
  • First-Order Function : 함수를 변수명에 저장(할당)

무엇보다도 아래의 코드가 모든 것을 말해주고 있다.

	print_h1 = html_tag('h1')
	print_h1('This is a h1 tag.')

우선, print_h1 = html_tag('h1')을 분석해보자.
문자열 'h1'을 html_tag라는 함수에 인자를 념겨 준 것이다.

그런데 html_tag 함수의 정의 부분을 보면
우리는 아직 변수 content의 값을 받지 못하여 알 수 없는 상태다.

바로 그 다음줄 코드를 바라보자.
정말 핵심 중 핵심인 부분이다.

    print_h1('This is a h1 tag')

print_h1 옆에 괄호를 열고 문자 입력이 가능한 것은 바로!!!
내장함수 content_area가 함수명을 return 했기 때문이다.
(우리는 함수명 자체로는 실행이 안된다고 앞에서 확인했다.)

따라서, 아래 코드까지의 상태는

    print_h1 = html_tag('h1')
  1. tag = 'h1'이 content_area라는 함수에 전달됐다.
  2. 변수 content는 아직 할당되지 않았다.
  3. 그래도 content_area라는 함수명을 리턴시키겠다.
    (함수명만 리턴하는 것이니, 함수를 실행시키진 않는다. 그래서 변수 content가 입력되지 않았다는 에러가 뜨지 않는다.)
  4. 변수 print_h1은 인자를 받을 준비가 된 content_area(내장함수)다.

수학적으로 표현재보자면
print_h1 == html_tag('h1') == content_area

따라서, 그 이후에

    print_h1('This is a h1 tag.')

는 결국,

print_h1('This is a h1 tag) == content_area('This is a h1 tag)

아무 생각없이 코드를 바라보면 어렵게 느껴지고, 이질적인 방법이다.
그러나 이 모든 복잡한 것을 가능케하고 이해시키는 열쇠는 바로

return 함수명



3) 정리

잠시 잊고 있었던 변수 tag에 대해 생각해보자.

사실 지금 시점에서 '변수 tag가 있었나?'와 같은 반응은 당연하다고 생각한다.

print_h1('안녕하세요'), print_h1('tHis Is A tAg') 등과 같이
변수 'content'를 이리저리 바꾸는 얘기만 했다.
즉, 자식함수(중첩함수)의 변수를 이것저것 바꿔본 연습을 했던 것이다.

그런데, 이번엔 변수 tag의 값을 수정하려고 하니 뭔가 막힌 느낌이 든다.
(접근이 어려워짐)

왜냐하면
tag='h1'이라는 값이 이미 content_area 함수에 전달됐기 때문이다.

언제 그랬냐면

    # 인자가 'h1'인 함수를 print_h1에 저장(할당)
    print_h1 = html_tag('h1')

따라서,
이처럼 '다른 사용자가 특정 변수에 접근하는 걸 어렵게 하고 싶을때' 사용할 수 있다.
이를 Closure라고 한다.( = 폐쇄)


[3] Closure

1) Closure란?

사실 위에서 다룬 개념들을 모두 이해했다면 Closure 개념이 더 이상 낯설지 않을 것이다.
오히려 이해하기가 수월해졌을 것이다.

위의 예제에서 다룬 코드를 다시 보자.

        print_h1 = html_tag('h1')
        print_h1('This is a h1 tag.')

설명1) print_h1 함수는 'h1' 이라는 인자를 갖고 있다.
설명 2) 그리고 다음줄에서 다시 'This is a h1 tag'라는 인자를 갖고 있다.

즉, 'h1'이라는 문자열 값이 그 아래 문자열로 대체되어 삭제된 것이 아니라
기억하고 있다.

2) Closure 사용조건

  1. 중첩함수가 있다.
  2. 그 중첩함수는 부모함수에서 가지고 있는 변수를 참조한다. ex) 'h1'
  3. 부모함수는 중첩함수명을 return 한다.

3) Closure는 사용목적

데이터들을 기반으로 연산을 수행하고 싶으나
기반이 되는 데이터에 다른 사람이 접근/수정 하는 걸 방지할 때 사용한다.

(참고) 물론 인수들이 많은 상황이거나 메소드 들이 많은 경우엔
클래스를 사용해서 데이터를 은닉시키는 것이 더 효율적일 수 있다.

4) 예시

#plus 함수 구현
def plus(*args):
    sum = 0
    for element in args:
        sum += element
    return sum
    
# minus 함수 구현
def minus(*args):
    sum = 0
    for element in args:
        sum -= element
    return sum


def set_function(func):

    def operation(*args):
        func_name = func.__name__
        result = func(*args)
        print(f'{func_name}함수의 연산 결과 : {result}')
    return operation

plus_func = set_function(plus)
plus_func(2, 3 ,4, 5, 8)

minus_func = set_function(minus)
minus_func(9, 8, 2)

결과
plus함수의 연산 결과 : 22
minus함수의 연산 결과 : -19



0개의 댓글