python / / 2024. 8. 19. 07:48

python에서 클로저(closure)란?

Python에서 클로저(Closure)는 함수형 프로그래밍의 중요한 개념 중 하나로, 함수가 정의될 때의 환경(변수 등)을 기억하고, 그 환경에 접근할 수 있는 함수 객체를 말한다. 클로저를 이해하면, 함수가 어떻게 자신이 선언된 환경을 유지하고, 그 환경에 접근할 수 있는지를 알 수 있다.


1. 클로저란?

클로저는 내부 함수가 외부 함수의 변수들에 접근할 수 있는 기능을 제공한다. 클로저는 일반적으로 함수가 함수 내부에 정의되고, 그 내부 함수가 외부 함수의 변수에 의존할 때 생성된다. 중요한 점은, 외부 함수가 실행을 마친 후에도 내부 함수가 외부 함수의 변수에 접근할 수 있다는 것이다.

2. 클로저의 동작 원리

클로저는 함수가 생성된 환경을 기억하고, 그 환경을 계속해서 참조할 수 있게 해준다. 이를 위해 내부 함수는 외부 함수의 지역 변수를 캡처한다.

기본 예제

다음은 클로저의 기본적인 예제이다.

def outer_function(message):
    def inner_function():
        print(message)
    return inner_function

closure = outer_function("Hello, World!")
closure()  # "Hello, World!" 출력

위 코드에서 outer_functionmessage라는 매개변수를 받고, inner_function이라는 내부 함수를 정의한 후 이를 반환한다. inner_functionmessage 변수를 참조하고 있다. outer_function이 호출되고 나면 message 변수는 더 이상 유효하지 않을 것 같지만, inner_function을 통해 message에 계속 접근할 수 있다. 이것이 클로저의 핵심이다.

3. 클로저의 활용 사례

클로저는 여러 가지 상황에서 유용하게 사용될 수 있다. 특히, 함수의 동작을 동적으로 변경하거나, 특정 상태를 유지해야 하는 경우에 자주 활용된다.

3.1. 상태 유지

클로저는 상태를 유지하는 함수(예: 카운터)를 만들 때 유용하다. 다음은 호출될 때마다 카운트를 증가시키는 클로저의 예이다.

def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

counter_a = make_counter()
print(counter_a())  # 1 출력
print(counter_a())  # 2 출력

counter_b = make_counter()
print(counter_b())  # 1 출력

여기서 make_countercount 변수를 포함하는 클로저를 생성한다. counter 함수는 countnonlocal로 선언하여, 외부 함수의 변수에 접근하고 값을 변경할 수 있게 한다.

3.2. 함수 동작의 커스터마이징

클로저를 사용하면 함수의 동작을 동적으로 커스터마이징할 수 있다. 예를 들어, 특정 기준으로 값을 필터링하는 함수를 생성할 수 있다.

def make_multiplier(x):
    def multiplier(n):
        return x * n
    return multiplier

times_two = make_multiplier(2)
print(times_two(5))  # 10 출력

times_three = make_multiplier(3)
print(times_three(5))  # 15 출력

위 예제에서 make_multiplier는 특정 값 x를 기준으로 다른 값을 곱하는 함수를 생성한다. 이 방식으로 클로저를 사용하면, 다양한 기준으로 동작하는 함수를 동적으로 생성할 수 있다.

3.3. 데이터 은닉

클로저는 데이터를 은닉하는 데도 사용할 수 있다. 외부에서 접근할 수 없는 비공개 데이터를 유지하면서, 필요한 정보만을 제공하는 메서드를 만들 수 있다.

def make_account(balance):
    def get_balance():
        return balance

    def deposit(amount):
        nonlocal balance
        balance += amount
        return balance

    def withdraw(amount):
        nonlocal balance
        if amount > balance:
            raise ValueError("Insufficient funds")
        balance -= amount
        return balance

    return get_balance, deposit, withdraw

get_balance, deposit, withdraw = make_account(100)
print(get_balance())  # 100 출력
print(deposit(50))    # 150 출력
print(withdraw(75))   # 75 출력

이 코드에서 balance 변수는 make_account 함수 내에 캡슐화되어 있으며, get_balance, deposit, withdraw 함수만이 이 변수에 접근할 수 있다. 이는 데이터 은닉을 통해 안전한 상태 관리가 가능하게 한다.

4. 클로저와 람다 함수

클로저는 람다(lambda) 함수와도 자주 결합되어 사용된다. 람다 함수는 간단한 한 줄짜리 함수로, 클로저를 사용하여 더 복잡한 동작을 표현할 수 있다.

람다 함수와 클로저 결합 예시

def make_power(n):
    return lambda x: x ** n

square = make_power(2)
print(square(5))  # 25 출력

cube = make_power(3)
print(cube(2))    # 8 출력

위 예제에서 make_power 함수는 n을 캡처하는 람다 함수를 반환한다. 이 클로저는 n의 값을 기억하고, 나중에 x에 대해 그 값을 사용한다.

5. 클로저의 장점과 단점

장점

  • 상태 유지: 클로저는 함수가 상태를 유지하고, 그 상태를 다른 함수 호출 시에도 사용할 수 있게 한다.
  • 데이터 은닉: 클로저를 사용하면 데이터가 외부에서 접근되는 것을 방지할 수 있다.
  • 유연성: 클로저는 함수의 동작을 동적으로 변경할 수 있게 하여, 더 유연한 코드 작성이 가능하게 한다.

단점

  • 디버깅 어려움: 클로저는 함수의 스코프가 복잡해질 수 있어 디버깅이 어려울 수 있다.
  • 메모리 사용: 클로저는 함수가 종료된 후에도 변수를 유지하기 때문에, 메모리 사용량이 늘어날 수 있다.
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유