파이썬의 클로저(Clsure) 사용법
파이썬의 클로저(Clsure) 사용법
파이썬의 클로저(Closure)는 함수가 정의된 환경을 기억하고, 그 환경에 속한 변수들을 함수가 호출될 때까지 유지하는 기능입니다. 간단히 말해, 내부 함수가 외부 함수의 변수를 참조하면서도 그 값을 유지할 수 있는 메커니즘입니다.
이번 글에서는 클로저의 개념과 사용하는 이유, 그리고 예제 코드를 통해 이를 어떻게 활용할 수 있는지 설명하겠습니다.
1. 클로저(Closure)란?
클로저는 함수 내부에 또 다른 함수를 정의하고, 그 내부 함수가 외부 함수의 변수를 참조할 때 발생합니다. 내부 함수는 외부 함수의 스코프(scope)에 있는 변수들을 기억하고, 외부 함수의 실행이 끝난 후에도 해당 변수를 사용할 수 있습니다.
클로저의 조건
클로저가 되기 위한 조건은 다음과 같습니다:
- 함수 내부에 또 다른 함수가 정의되어야 함.
- 내부 함수가 외부 함수의 변수를 참조해야 함.
- 외부 함수가 종료된 후에도 참조한 변수를 사용할 수 있어야 함.
기본 구조
def outer_function():
= 10 # 외부 함수의 변수
x
def inner_function():
print(x) # 내부 함수가 외부 함수의 변수를 참조
return inner_function
위 예시에서 inner_function
은 outer_function
의 변수 x
를 참조합니다. outer_function
이 종료된 후에도 inner_function
은 x
를 기억하고 사용할 수 있습니다.
2. 클로저를 사용하는 이유
클로저는 데이터를 캡슐화하고 상태를 기억하는데 유용합니다. 외부 함수의 변수를 기억하는 기능 덕분에, 클로저는 마치 객체의 속성처럼 데이터를 저장하고, 상태를 유지할 수 있습니다.
클로저를 사용하는 이유는 다음과 같습니다:
- 상태 유지: 함수가 호출된 이후에도 외부 함수의 상태(변수)를 유지할 수 있습니다.
- 데이터 은닉: 클로저를 통해 외부에서 직접 접근할 수 없는 변수를 사용할 수 있습니다.
- 함수형 프로그래밍: 클로저는 고차 함수나 콜백 함수에서 자주 사용됩니다. 이러한 함수형 프로그래밍 패턴에서 클로저는 중요한 역할을 합니다.
3. 클로저 사용 예시
예시 1: 기본 클로저 예시
def outer_function():
= 10 # 외부 함수 변수
x
def inner_function():
print(f"외부 함수의 변수 x: {x}") # 외부 변수 참조
return inner_function # 내부 함수 반환
# 외부 함수를 호출하고 내부 함수를 반환받음
= outer_function()
closure
# 클로저 함수 실행
# 외부 함수가 종료되었음에도 x 값을 출력 closure()
출력:
외부 함수의 변수 x: 10
이 예시에서 outer_function
이 종료된 후에도 inner_function
은 x
변수를 기억하고 있습니다. closure()
를 호출하면, 외부 함수의 변수 x
를 사용할 수 있습니다.
예시 2: 상태를 기억하는 클로저
클로저는 호출될 때마다 상태를 기억하고 그 상태를 바탕으로 동작할 수 있습니다. 이를 통해, 외부 함수의 변수를 마치 객체의 속성처럼 사용할 수 있습니다.
def make_multiplier(n):
def multiplier(x):
return x * n # 외부 함수 변수 n을 참조
return multiplier
# 2배수와 3배수를 계산하는 클로저 생성
= make_multiplier(2)
times_two = make_multiplier(3)
times_three
# 클로저 실행
print(times_two(10)) # 20
print(times_three(10)) # 30
출력:
20
30
이 예시에서 make_multiplier
는 내부 함수 multiplier
를 반환하며, n
값을 기억합니다. times_two
는 n
이 2인 상태를 유지하고, times_three
는 n
이 3인 상태를 유지합니다. 이를 통해 다양한 배수를 계산하는 함수들을 생성할 수 있습니다.
예시 3: 클로저를 사용한 데이터 은닉
클로저는 데이터 은닉에도 사용될 수 있습니다. 외부 함수의 변수를 클로저로 감싸서, 해당 변수에 대한 직접적인 접근을 막을 수 있습니다.
def bank_account(initial_balance):
= initial_balance # 외부 함수의 변수
balance
def deposit(amount):
nonlocal balance
+= amount
balance return balance
def withdraw(amount):
nonlocal balance
if balance >= amount:
-= amount
balance return balance
else:
return "잔액 부족"
return deposit, withdraw
# 클로저로 은행 계좌 생성
= bank_account(100)
deposit_fn, withdraw_fn
print(deposit_fn(50)) # 입금 후 잔액: 150
print(withdraw_fn(30)) # 출금 후 잔액: 120
print(withdraw_fn(200)) # 잔액 부족
출력:
150
120
잔액 부족
이 예시에서 balance
변수는 외부에서 직접 접근할 수 없습니다. 오직 클로저로 반환된 deposit
과 withdraw
함수만이 balance
에 접근하고 수정할 수 있습니다. 이렇게 하면 중요한 데이터를 은닉하고, 특정 함수로만 접근하도록 제한할 수 있습니다.
4. 클로저와 nonlocal
키워드
클로저 내부에서 외부 함수의 변수를 수정하려면 nonlocal
키워드를 사용해야 합니다. nonlocal
은 외부 함수에 선언된 변수를 내부 함수에서 참조하고 수정할 수 있도록 해줍니다.
예시: nonlocal
사용
def outer_function():
= 10 # 외부 함수 변수
x
def inner_function():
nonlocal x # 외부 함수의 변수를 수정
+= 1
x return x
return inner_function
= outer_function()
closure print(closure()) # 11
print(closure()) # 12
출력:
11
12
여기서 nonlocal
키워드를 사용하여 x
값을 수정할 수 있습니다. closure()
를 호출할 때마다 x
가 1씩 증가합니다.
5. 클로저와 객체 지향 프로그래밍 비교
클로저는 상태를 유지하고 데이터를 은닉하는 면에서 객체 지향 프로그래밍(OOP)의 객체와 유사합니다. 하지만, 클로저는 함수를 통해 상태를 유지하는 반면, 객체는 클래스와 메서드를 사용합니다. 클로저는 가볍고 간단한 상태 관리를 위해 사용되며, OOP에서 클래스를 사용하는 것보다 더 간결한 표현을 제공합니다.
클로저 vs 객체 지향 예시
클로저와 클래스의 차이를 간단한 카운터 예시로 비교해 보겠습니다.
클로저를 사용한 카운터
def counter():
= 0
count
def increment():
nonlocal count
+= 1
count return count
return increment
= counter()
count_fn print(count_fn()) # 1
print(count_fn()) # 2
출력
1
2
클래스를 사용한 카운터
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
return self.count
= Counter()
counter_obj print(counter_obj.increment()) # 1
print(counter_obj.increment()) # 2
출력
1
2
이 두 코드는 같은 기능을 하지만, 클로저는 더 간단하고 함수적인 방식으로 상태를 유지합니다. 반면, 클래스는 더 명시적인 객체 구조를 사용합니다.
결론
파이썬의 클로저는 함수가 정의된 환경을 기억하고, 그 환경 내의 변수들을 계속 사용할 수 있는 강력한 도구입니다. 클로저를 사용하면 데이터를 은닉하고, 상태를 기억하며, 반복적인 동작을 간결하게 구현할 수 있습니다. 객체 지향 프로그래밍과 비슷한 기능을 제공하지만, 더 가볍고 함수적인 패턴을 선호하는 경우 클로저를 활용하는 것이 좋습니다.
클로저는 함수형 프로그래밍에서 자주 사용되며, 상황에 맞게 클로저를 활용하면 코드의 유연성과 재사용성을 높일 수 있습니다!