프로그래밍파이썬 개발자

파이썬에서 클로저(closures)는 어떻게 작동하나요? 외부 함수의 변수에 접근하는 데 있는 특징은 무엇이며, 가변 변수와 관련된 함정은 어떻게 피할 수 있나요?

Hintsage AI 어시스턴트로 면접 통과

답변.

클로저(closures)는 파이썬의 강력한 메커니즘으로, 주변 컨텍스트에서 "기억된" 데이터를 가진 함수를 생성할 수 있게 해줍니다.

질문의 역사

함수형 언어와 파이썬에서 함수는 일급 객체입니다. 함수는 자신을 둘러싼(외부) 범위의 변수를 '클로저'하는 새 함수 반환할 수 있습니다.

문제

개발자들은 종종 클로저 내부의 클로저된 변수가 어떻게 "존재하는지", 읽히거나 쓰일 때 어떻게 작동하는지, 그리고 이를 안전하게 (특히 가변 객체와 함께) 어떻게 다루는지 혼동합니다.

해결책

내부 함수가 외부 함수의 변수를 사용하는 경우, 이 변수들은 자동으로 "기억됩니다" — 외부 함수가 이미 종료되었더라도 말이죠. 변수를 읽는 것은 명백하지만, 값을 변경해야 하는 경우에는 nonlocal 키워드를 사용해야 합니다. 가변 객체(list, dict)와 함께 작업할 때는 특히 위험한 지역입니다.

예시:

def outer(): count = 0 def inner(): nonlocal count count += 1 return count return inner counter = outer() print(counter()) # 1 print(counter()) # 2

주요 특징:

  • 내부 함수는 생성된 시점의 변수 값들을 기억합니다.
  • 클로저된 변수를 변경할 때는 nonlocal을 사용하세요 (그렇지 않으면 지역 변수를 생성하게 됩니다).
  • 클로저 내의 객체에 대한 참조는 외부 함수에서 나간 후에도 유지되어, 함수 팩토리, 렉시컬 카운터 등을 구현할 수 있게 합니다.

트릭 질문.

nonlocal 없이 클로저된 변수의 값을 변경할 수 있나요?

아니요. 새로운 값을 할당하려고 시도하면, nonlocal을 지정하지 않으면 파이썬은 이를 새 지역 변수로 인식하고, 오래된 값은 외부로 나가지 않습니다.

예시:

def make_counter(): count = 0 def inner(): count += 1 # UnboundLocalError 오류! return count return inner

클로저를 통해 외부 범위에 인수를 전달할 수 있나요?

예, 클로저는 외부 범위에서 접근할 수 있는 모든 변수를 "기억합니다", 클래스 속성, 전역 변수 등을 포함합니다. 하지만 이러한 변수를 변경하려면 특별한 노력이 필요합니다 (예: nonlocal 또는 global 사용).

클로저 내부의 가변 객체는 어떻게 되나요?

가변 객체에 대한 참조가 클로저에 클로저될 경우, 예를 들어 리스트의 경우, nonlocal 없이 그 내용을 수정할 수 있지만, 변수를 재할당하려고 하면 nonlocal이 필요합니다.

예시:

def make_appender(): result = [] def append(x): result.append(x) # 가능합니다! return result return append f = make_appender() print(f(1)) # [1] print(f(2)) # [1, 2]

흔한 오류 및 안티 패턴

  • nonlocal 없이 클로저 내에서 변수를 재할당하려고 시도.
  • 가변 상태를 저장하기 위해 클로저를 사용하지만 메모리 누수 가능성을 인식하지 못함.
  • 클로저된 변수가 너무 많아 코드 가독성이 떨어짐.

실생활 예

부정적 케이스

개발자가 클로저를 작성하고 nonlocal 없이 변수를 변경하여 UnboundLocalError가 발생합니다.

장점:

  • 카운터, 팩토리의 빠른 프로토타입 제작.

단점:

  • nonlocal에 대한 실수로 인한 예측 불가 행동, 버그.

긍정적 케이스

상태 관리를 위한 클로저에서 nonlocal을 명시적으로 사용하는 경우.

장점:

  • 명확하게 제어되는 상태, 카운터 및 함수 팩토리 구현이 용이합니다.

단점:

  • 대규모 클로저 체인을 이해하고 디버깅하기가 더 어려워집니다.