Higher-Order Function #2 closure

정은경·2020년 5월 3일

1. 학습목표

2. 학습내용

# Decorator & Closure
# 일급함수(일급객체)를 지원하기때문에 decorator와 closure를 쓸 수 있음!

# 파이썬 변수 범위(global)

# 예제1
def func_v1(a):
    print(a)
    print(b)

# 예외 에러 발생
# func_v1()
"""
Traceback (most recent call last):
  File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 11, in <module>
    func_v1()
TypeError: func_v1() missing 1 required positional argument: 'a'

"""

# 예제2
b = 10

def func_v2(a):
    print(a)
    print(b)

# func_v2(5)
"""
5
10
"""


# 예제3
b = 10

def func_v3(a):
    print(a)
    print(b)
    b = 5 # b라는 변수에 값을 할당 전에 출력하려고 해서 에러 발생! 지역변수가 글로벌변수 보다 먼저!

# func_v3(5)
"""
5
Traceback (most recent call last):
  File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 43, in <module>
    func_v3(5)
  File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 40, in func_v3
    print(b)
UnboundLocalError: local variable 'b' referenced before assignment
"""


# 실행의 흐름 확인하기
from dis import dis

# print ('EX1-1 -', dis(func_v3))
"""
 39           0 LOAD_GLOBAL              0 (print)
              2 LOAD_FAST                0 (a)
              4 CALL_FUNCTION            1
              6 POP_TOP

 40           8 LOAD_GLOBAL              0 (print)
             10 LOAD_FAST                1 (b)
             12 CALL_FUNCTION            1
             14 POP_TOP

 41          16 LOAD_CONST               1 (5)
             18 STORE_FAST               1 (b)
             20 LOAD_CONST               0 (None)
             22 RETURN_VALUE
EX1-1 - None
"""


# Closure(클로저)
# 반환되는 내부 함수에 대해서 선언된 연결 정보를 가지고 참조하는 방식
# 반환 당시 함수 유효범위를 벗어난 변수 또는 메소드에 직접 접근이 가능한다

a = 10
# print('EX2-1 -', a+10)
# EX2-1 - 20
# print('EX2-2 -', a+100)
# EX2-2 - 110

# 결과를 누적할 수 있을 까?
# print('EX3-1 -', sum(range(1,51)))
# EX3-1 - 1275
# print('EX3-2 -', sum(range(51,101)))
# EX3-2 - 3775


# 클래스를 이용해서 누적하기
class Averager():
    def __init__(self):
        self._series = []

    def __call__(self, v):
        self._series.append(v)
        print('class >>> {}/{}'.format(self._series, len(self._series)))
        return sum(self._series) / len(self._series)


# 인스턴스 생성
avg_cls = Averager()


# 누적 확인
# 누적이 가능한 이유는 메소드 밖의 변수(클로저 영역의 변수)에 저장하고 있기 때문!!!
# print('EX3-1 -', avg_cls(15))
"""
class >>> [15]/1
EX3-1 - 15.0
"""
# print('EX3-2 -', avg_cls(35))
"""
class >>> [15, 35]/2
EX3-2 - 25.0
"""
# print('EX3-2 -', avg_cls(50))
"""
class >>> [15, 35, 50]/3
EX3-2 - 33.333333333333336
"""


# 클로저(closure) 사용함
# 전역변수 사용 감소
# 디자인 패턴 적용
def closure_avg1():
    # Free Variable : 외부 함수와 내부 함수 사이의 영역을 free variable이라고 함
    series = []
    # 클로저 영역
    # 클로저 영역을 사용하면 함수형 프로그래밍이나 마우스 클릭수 저장하기 등에 사용용
    # 변수에 대한 은닉화도 가능
    # 클로저 사용시에 조심할 부분들은 있음! 자원 낭비 조심! 꼭 필요한 곳에 사용하도록!
    # 클로저 영역의 변수는 함수의 실행이 종료되어도 접근이 가능한 것이 포인트..!

    def averager(v):
        series.append(v)
        print('def >>> {}/{}'.format(series, len(series)))
        return sum(series) / len(series)
    return averager

avg_closure1 = closure_avg1()
# print('EX4-1 -', avg_closure1)
# EX4-1 - <function closure_avg1.<locals>.averager at 0x102de1bf8>
# print('EX4-2 -', avg_closure1(15))
"""
def >>> [15]/1
EX4-2 - 15.0
"""
# print('EX4-2 -', avg_closure1(35))
"""
def >>> [15, 35]/2
EX4-2 - 25.0
"""
# print('EX4-2 -', avg_closure1(40))
"""
def >>> [15, 35, 40]/3
EX4-2 - 30.0
"""

# print('EX5-1 -', dir(avg_closure1))
"""
EX5-1 - ['__annotations__', '__call__', 
'__class__', '__closure__', '__code__', 
'__defaults__', '__delattr__', 
'__dict__', '__dir__', '__doc__', 
'__eq__', '__format__', '__ge__', 
'__get__', '__getattribute__', '__globals__', 
'__gt__', '__hash__', '__init__', 
'__init_subclass__', '__kwdefaults__', 
'__le__', '__lt__', '__module__', 
'__name__', '__ne__', '__new__', '__qualname__', 
'__reduce__', '__reduce_ex__', '__repr__', 
'__setattr__', '__sizeof__', '__str__', '__subclasshook__']
"""


# print('EX5-2 -', dir(avg_closure1.__code__))
"""
EX5-2 - ['__class__', '__delattr__', '__dir__', 
'__doc__', '__eq__', '__format__', '__ge__', 
'__getattribute__', '__gt__', '__hash__', 
'__init__', '__init_subclass__', 
'__le__', '__lt__', 
'__ne__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', 
'__setattr__', '__sizeof__', '__str__', 
'__subclasshook__', 
'co_argcount', 'co_cellvars', 'co_code', 
'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 
'co_freevars', 
'co_kwonlyargcount', 'co_lnotab', 
'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
"""

# print('EX5-3 -', avg_closure1.__code__.co_freevars)
# EX5-3 - ('series',)

# print('EX5-4 -', dir(avg_closure1.__closure__[0]))
"""
EX5-4 - ['__class__', '__delattr__', '__dir__', '__doc__', 
'__eq__', '__format__', '__ge__', '__getattribute__', 
'__gt__', '__hash__', '__init__', '__init_subclass__', 
'__le__', '__lt__', '__ne__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', 
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'cell_contents']
"""

# print('EX5-4 -', dir(avg_closure1.__closure__[0].cell_contents))
"""
EX5-4 - ['__add__', '__class__', '__contains__', 
'__delattr__', '__delitem__', 
'__dir__', '__doc__', '__eq__', '__format__', '__ge__', 
'__getattribute__', '__getitem__', 
'__gt__', '__hash__', '__iadd__', '__imul__', 
'__init__', '__init_subclass__', '__iter__', 
'__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__reversed__', 
'__rmul__', '__setattr__', '__setitem__', 
'__sizeof__', '__str__', '__subclasshook__', 
'append', 'clear', 'copy', 'count', 'extend', 'index', 
'insert', 'pop', 'remove', 'reverse', 'sort']
"""

# 클로저를 잘못한 사용한 예제들
def closure_avg2():
    # Free Variable
    cnt = 0
    total = 0
    # 클로저 영역

    def averager(v):
        cnt += 1
        total += v
        return total/cnt
    return averager

avg_closure2 = closure_avg2()

# print('EX5-5 -', avg_closure2(15))
"""
/Users/marie/PycharmProjects/untitled1/venv/bin/python /Users/marie/PycharmProjects/untitled1/fc_lecture.py
Traceback (most recent call last):
  File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 245, in <module>
    print('EX5-5 -', avg_closure2(15))
  File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 238, in averager
    cnt += 1
UnboundLocalError: local variable 'cnt' referenced before assignment
"""


def closure_avg3():
    # Free Variable
    cnt = 0
    total = 0
    # 클로저 영역

    def averager(v):
        nonlocal cnt, total
        # free variable가 동일한 이름의 지역 변수가 있으면 지역 변수가 우선!
        # 그럴때 클로저를 쓰고 싶다면! nolocal 키워드로 클로저 변수로 쓸 변수의 이름을 지정하세욤!

        cnt += 1
        total += v
        print('def2>>> {} / {}'.format(total, cnt))
        return total/cnt
    return averager

avg_closure3 = closure_avg3()
# print('EX5-6 -', avg_closure3(15))
"""
def2>>> 15 / 1
EX5-6 - 15.0
"""
# print('EX5-7 -', avg_closure3(35))
"""
def2>>> 50 / 2
EX5-7 - 25.0
"""

3. 느낀 점

profile
#의식의흐름 #순간순간 #생각의스냅샷

0개의 댓글