인프런 커뮤니티 질문&답변

힌턴님의 프로필 이미지
힌턴

작성한 질문수

우리를 위한 프로그래밍 : 파이썬 중급 (Inflearn Original)

일급함수 - 클로저 심화(3-2)

클로저 예시 함수에서 nonlocal 사용이 필요한 여부에 대해 질문드립니다.

작성

·

332

·

수정됨

1

안녕하세요. 강의 감사히 잘 듣고 있는 수강생입니다.

클로저 관련 내용을 듣다 궁금한 점이 있어 질문 드립니다.

해당 문제와 관련하여 다른 분들도 동일한 질문을 여러 번 올려주셨는데 "명확하게 이렇기 때문이다"라는 답변이 없이 외부 링크를 걸어주시거나 단답형으로 답변을 주셨던 것 같아 다시 질문을 드립니다.

def closure_ex1():
    # Free variable
    series = []
    # 클로저 영역
    def averager(v):
        # series = [] # 주석 해제 후 확인
        series.append(v)
        print('inner >>> {} / {}'.format(series, len(series)))
        return sum(series) / len(series)
    
    return averager


avg_closure1 = closure_ex1()
# 잘못된 클로저 사용
def closure_ex2():
    # Free variable
    cnt = 0
    total = 0

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

avg_closure2 = closure_ex2()

위 두 함수 closure_ex1과 closure_ex2를 비교하면 차이는

  1. series는 list, cnt와 total은 int형 변수라는 것,

  2. averager 함수 내부에서 series는 append 작업을 하고, cnt와 total은 값을 더해주는 작업을 한다는 것

입니다. 그런데 closure_ex1에서는 오류가 나지 않고, closure_ex2에서는 averager 안에 nonlocal cnt, total을 작성하지 않으면 오류가 납니다.

여쭤보고 싶은 것은,

[1] closure_ex1의 averager 함수 내부에 nonlocal series 라는 코드를 작성하지 않아도 되는 이유가 무엇 때문인가 입니다.

closure_ex2의 averager 안에 nonlocal cnt, total 이 필요한 것은 내부 함수의 영역은 local 영역이고 closure_ex2 내부이면서 average 외부인 영역은 nonlocal 영역이기 때문에 local 영역에서 free variable을 write 작업하기 위해서는 nonlocal 변수 선언이 필요한 것으로 알고 있습니다. 하지만 series 변수에 대해서는 왜 nonlocal series 라는 코드가 필요하지 않은 것인지요?

제가 추가로 공부할 링크를 주시는 것은 감사하지만, 이 질문에 대한 답변을 명확하게 댓글로 작성해주시면 감사드리겠습니다. 다른 분들의 질문에 올려 주신 링크는 모두 읽어보았습니다. 또한 명확한 답변을 주시면 공부하시는 다른 분들께도 유용할 것이라고 생각합니다. 감사합니다.

ps. 강의 영상을 확인하라는 답변도 주셨었는데, 수업에서 정확하게 list나 int형 자료의 scope 별 생명 주기까지 설명한 내용은 찾지 못하였으니 만약 해당 내용이 필요하다면 강의 시간을 알려주시면 감사드리겠습니다.

 

답변 1

0

좋은사람님의 프로필 이미지
좋은사람
지식공유자

안녕하세요.

코드를 아래와 같이 바꿔보겠습니다.

에러가 발생하지 않습니다.

def closure_ex2():

# Free variable

cnt = 0

total = 0

def averager(v):

cnt = 10

total = 0

total += v

return total / cnt

 

return averager

avg_closure2 = closure_ex2()

print(avg_closure2(10))

 

내부 average 함수에서 기존코드인 cnt += 1 부분은 cnt = cnt + 1과 같습니다.

지역함수내에서 최초에 cnt 변수를 찾는데 외부 함수인 clousure_ex2에 선언되어 있습니다.

파이썬에서는 내부 기존 변수인 cnt 에 새로운 값을 할당을 시도하기 때문에 로컬 변수로 인식하게 되고

내부함수내에서 초기화 되지 않은 cnt 에 값을 할당하려고 하기 때문에

UnboundLocalError: cannot access local variable 'cnt' where it is not associated with a value

에러가 발생하게 됩니다. -> 이 때, nonlocal을 사용해서 해당 지역안에서만 사용하는 변수라는 키워드를 사용하기도 합니다.

위 코드는 cnt 를 내부함수에서 정확하게 0으로 초기화 하고 사용했기 때문에 에러가 발생하지 않습니다.

closure_ex2에 선언되어 있는 변수는 id함수로 출력을 해보시면 서로 다른 id 값이 나오는 것을

확인할 수 있을거예요.print(id(cnt))

에러가 발생하면 해당 에러 내용으로 검색을 해보시는 것을 추천드립니다.

개발을 할 경우는 많은 검색을 통해서 지식을 얻는 경우가 많아서요.

아울러 지금 질문주신 내용과 정확하게 일치하는 블로그를 링크를 드니 한 번 꼭 읽어보세요.

https://sikaleo.tistory.com/99

힌턴님의 프로필 이미지
힌턴
질문자

안녕하세요, 강의자님. 답변 감사드립니다. 많은 도움이 되었습니다.

다만 제가 여쭤보고 싶은 것으로 표시한 내용에서처럼, cnt가 그런 것은 이해하겠지만 왜 series에 대해서는 동일한 논리를 적용할 수 없는 것인지입니다.

id 값을 말씀해주셨는데, 위 코드에서 series는 nonlocal과 local 영역에서 동일한 id를 가지고 있기 때문에 cnt 변수의 경우와는 다른 케이스라는 말씀이신지요?

감사합니다.

좋은사람님의 프로필 이미지
좋은사람
지식공유자

series 는 일반변수가 아닌 참조형 변수입니다.(리스트) 아래 값을 할당하는 것이 아닌

내부의 값을 변경하는 것이라 에러가 발생하지 않아요~

https://stackoverflow.com/questions/8447947/is-it-possible-to-modify-a-variable-in-python-that-is-in-an-outer-enclosing-b

def foo():
    a = []
    def bar():
        a.append(1)
    bar()
    bar()
    print(a)

foo()
힌턴님의 프로필 이미지
힌턴
질문자

상세한 답변 감사드립니다. 보내주신 링크들 보고 열심히 공부하겠습니다.

남은 강의도 감사히 잘 듣겠습니다.

좋은사람님의 프로필 이미지
좋은사람
지식공유자

네네 홧팅입니다.

힌턴님의 프로필 이미지
힌턴

작성한 질문수

질문하기