파이썬 심화(클로저)

팔리동·2021년 5월 10일

🐍파이썬 클로저

전역변수 참조 예시

def func_v1(a):
    print(a)
    print(b)

func_v1(5)    
# 오류 
  • 위 코드와 같이 print(a), print(b)를 실행하는 함수를 만들고 인자에 5를 넣어서 호출하면 오류가난다.
  • 이유는 b값이 없기 때문이다.
b = 10 

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

func_v2(5)
# 5 10 
  • 전역변수로 b = 10을 할당하면
  • 오류가 나지않고 5와 10을 출력한다.

하지만 !

b = 10

def func_v3(a):
    print(a)
    print(b)
    b = 5

func_v3(5)
# 5 
# 오류 
  • 전역변수를 선언하고 할당하고
  • 지역변수에 b = 5 라고 설정하고 함수를 실행하면 오류가난다.
  • 지역변수가 있으면 전역변수를 참조하지 않는다.
  • 지역변수가 print(b)보다 아래에 있어서 오류가 난다.

클로저(closure)

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

코드로 확인해보자

클래스 사용 예제

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(avg_cls(10)) 
# class >>> [10] / 1
# 10.0
print(avg_cls(20))
# class >>> [10, 20] / 2
# 15.0
print(avg_cls(30))
# class >>> [10, 20, 30] / 3
# 20.0 
  • 값이 누적된다.

클로저 사용 예제

def closure_avg1():
    # Free variable
    series = []
    # 클로저 영역 
    def averager(v):
        # series = [] # check
        series.append(v)
        print('def >>> {} / {}'.format(series, len(series)))
        return sum(series) / len(series)
    return averager  # 함수를 호출만 한다. 
  • 함수안에 함수 사이의 변수를 Free variable이라고 한다.
  • 함수안의 함수를 클로저 영역이라고 한다.
  • closure_avg1함수의 리턴값에 averager함수명만 넣는다.
    • closure_avg1함수를 객체에 담으면 averager함수를 반환한다.

누적 확인

avg_closure1 = closure_avg1() # 함수 담기 

print(avg_closure1(15))
# def >>> [15] / 1
# 15.0
print(avg_closure1(35))
# def >>> [15, 35] / 2
# 25.0
print(avg_closure1(40))
# def >>> [15, 35, 40] / 3
# 30.0
  • 클로저를 사용하면 전역변수 사용율이 감소한다.

잘못 된 클래스 사용 예

def closure_avg2():
    # Free variable
    cnt = 0 
    total = 0
    # 클로저 영역 
    def averager(v):     
        cnt += 1 
        total += v 
        print('def2 >>> {} / {}'.format(total, cnt))
        return total / cnt 
    return averager 

avg_closure2 = closure_avg2()
print(avg_closure2(15)) # 오류 
print(avg_closure2(35)) # 오류 
print(avg_closure2(40)) # 오류 
  • 위 코드에서 보면 변수 cnt와 total이 averager함수에서 선언이 되어있지 않아서 오류가 난다.

  • Free variable을 참조하지 않는다.

    오류: UnboundLocalError: local variable 'cnt' referenced before assignment

  • 해결방법은?

    • averager함수에 nonlocal cnt, total 을 선언해서 변수를 참조하게 한다.

수정 코드

```python
def closure_avg2():
    # Free variable
    cnt = 0 
    total = 0
    # 클로저 영역 
    def averager(v):    
    	nonlocal cnt, total
        cnt += 1 
        total += v 
        print('def2 >>> {} / {}'.format(total, cnt))
        return total / cnt 
    return averager 

avg_closure2 = closure_avg2()
print(avg_closure2(15)) 
def2 >>> 15 / 1
15.0
print(avg_closure2(35)) 
def2 >>> 50 / 2
25.0
print(avg_closure2(40))
def2 >>> 90 / 3
30.0
profile
배움의 기록

0개의 댓글