파이썬(Python)

파이썬 - 함수 추가 내용(2)

BlueNoa 2022. 12. 14. 16:46
728x90
반응형

·Closure

클로저란 Scope에 제약을 받지 않는 변수들을 포함하고 있는 코드 블록을 뜻한다.

여기서 스코프(Scope)란 사전적인 의미를 찾아보면 '(주체조직활동 등이 다루는) 범위'라고 한다.

즉, 정리하자면 내부함수의 주소를 변환해 함수 밖에서도 함수의 내부 변수를 계속 참조하는 기술이다.

 

예를 들어서 다음과 같은 함수를 지정해서 함수 내부의 변수를 출력하려 하면 다음과 같은 결과가 출력된다.

def Fn(a, b):
    c = a * b
    return c

print(Fn(3, 5))
print(c) # 함수 내부에서 선언한 변수이기 때문에 오류가 발생한다.
15
NameError: name 'c' is not defined

 

이렇듯 함수 내부에서 선언된 변수는 외부에서 사용할 수 없다.

그와 관련된 설명을 하기 전에 함수는 생성될 때 고유의 id를 갖고 있고 이에 대한 특징 살펴보자.

 

val_1 = Fn(2, 3)
print(val_1) # 함수의 계산 결과가 출력
val_1 = Fn # 함수의 주소를 'Val_1'이라는 변수에게 할당
print(val_1) # 주소가 출력된다.
print(val_1(4, 2)) # 원하는 매개변수를 할당할 수 있다.
print(id(val_1), id(Fn)) # 둘의 주소가 같다.(실행되는 컴퓨터마다 다를 수 있음)
del Fn # 함수 객체 자체를 지우는 것이 아니라 참조하던 주소를 삭제한다.
print(id(val_1), id(Fn)) # 참조하던 주소가 삭제되었기 때문에 오류가 발생한다.
<function Fn at 0x00000254C42A7280>
8
2563091624576 2563091624576
8
NameError: name 'Fn' is not defined

 

그렇다면 여기서 클로저를 사용했을 경우와 사용하지 않은 경우를 비교해보자.

- 클로저를 사용하지 않은 경우

def outside():
    count = 0 # 지역 변수
    def inside():
        nonlocal count # 비전역 변수(outside의 변수)
        count += 1
        return count
    print(inside())
# print(count) # error 발생, 지역변수이기 때문이다.
var1 = outside()
print(var1)
# print(var1()) # error 발생
1
None
TypeError: 'NoneType' object is not callable

클로저가 적용되지 않아 함수 내의 변수를 호출하면 에러가 발생하는 것을 알 수 있다.

함수 내부의 count의 값이 계속해서 더할 수 없이 1회성으로 더한후에는 'print(var1())'을 하면 오류가 발생하는 것을 알 수 있다.

 

- 클로저를 사용한 경우

def outside():
    count = 0
    def inside():
        nonlocal count
        count += 1
        return count
    return inside # inside 내부함수의 주소를 반환하므로 외부에서도 범위의 관계 없이 호출이 가능해진다.

var2 = outside()
print(var2) # inside 내부함수 객체 주소
print(var2())
print(var2())
print(var2()) 

var3 = outside() # 별도의 객체로 생성
print(var3())
print(var3())

print(id(var2), id(var3)) # 두 변수의 주소가 다르다는 것을 알 수 있다.
<function outside.<locals>.inside at 0x0000023057E61C10>
1
2
3
1
2
2406656384016 2406656384880

보는 봐와 같이 내부함수인 'inside'의 주소를 return 해주면 var2에는 리턴된 inside의 주소를 받게 된다.

또 새로운 변수 var3에게 다시 'inside'의 주소를 받아오기 때문에 var2와 var3는 서로 다른 주소를 갖고 축척(덧셈)된 값이 서로 다르게 갖고 있을 수 있다.

 

ex) 물건을 구매할 때 수량과 단가, 세금을 이용하여 물건의 값을 계산하는 예제

print('수량 * 단가 * 부가가치세을 출력하는 함수 : 클로저 사용')
def outer_def(VAT):
    def inner_def(su, dan):
        amount = su * dan * VAT
        return amount
    return inner_def # <=== 클로저 역할

# tax = 0.1
r1 = outer_def(0.1) # inner_def 함수 객체의 주소를 기억, 0.1 VAT를 할당
result1 = r1(5, 50000) # outer_def의 지역변수인 VAT를 사용해서 계산
print('result1 : ', result1) # 계산 결과 출력

result2 = r1(2, 10000) # outer_def의 지역변수인 VAT를 사용
print('result2 : ', result2)

# tax = 0.25
r2 = outer_def(0.05) # inner_def 함수 객체의 주소를 기억
result3 = r2(5, 50000) # outer_def의 지역변수인 VAT를 사용
print('result3 : ', result3)
수량 * 단가 * 부가가치세을 출력하는 함수 : 클로저 사용
result1 :  25000.0
result2 :  2000.0
result3 :  12500.0

 

728x90
반응형