파이썬 프로그래밍 (6) - 파이썬 클로저 & 데코레이터

이영락·2024년 8월 1일

개발자 기본기

목록 보기
6/53

클로저와 데코레이터는 파이썬에서 중요한 개념으로, 함수와 클래스의 활용도를 높여주는 기능이다.

🏖️ 클로저(Closure)

클로저는 함수 내부에 또 다른 함수를 정의하고, 이 내부 함수를 반환하는 구조를 말한다. 클로저를 사용하면 외부 함수의 변수를 내부 함수에서 기억하고 사용할 수 있다.

예제 1: 단순 함수 정의

def mul3(n):
    return n * 3

def mul5(n):
    return n * 5

위 코드에서는 mul3, mul5처럼 특정 값을 곱하는 함수들을 각각 정의하고 있다. 하지만 비슷한 함수들을 계속 만드는 것은 비효율적이다.

예제 2: 클래스 사용

class Mul:
    def __init__(self, m):
        self.m = m

    def __call__(self, n):
        return self.m * n

mul3 = Mul(3)
mul5 = Mul(5)

print(mul3(10))  # 30 출력
print(mul5(10))  # 50 출력

클래스를 사용하면 특정 값을 미리 설정해 두고, 객체를 통해 그 값을 곱하는 메서드를 사용할 수 있다.

예제 3: 클로저 사용

def mul(m):
    def wrapper(n):
        return m * n
    return wrapper

mul3 = mul(3)
mul5 = mul(5)

print(mul3(10))  # 30 출력
print(mul5(10))  # 50 출력

클로저를 사용하면 내부 함수 wrapper가 외부 함수의 인자 m을 기억해, 클래스 없이도 비슷한 기능을 구현할 수 있다.

🏖️ 데코레이터(Decorator)

데코레이터는 기존 함수에 새로운 기능을 추가할 수 있는 방법이다. 이를 통해 기존 코드를 수정하지 않고도 기능을 확장할 수 있다.

예제 1: 함수의 실행 시간을 측정하는 데코레이터

import time

def elapsed(original_func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = original_func(*args, **kwargs)
        end = time.time()
        print("함수 수행시간: %f 초" % (end - start))
        return result
    return wrapper

@elapsed
def myfunc(msg):
    print("'%s'을 출력합니다." % msg)

myfunc("You need python")

위 코드에서 elapsed 데코레이터는 함수의 실행 시간을 측정해준다. @elapsed를 사용해 myfunc 함수에 이 데코레이터를 적용하면, myfunc가 실행될 때마다 자동으로 수행 시간이 출력된다.

# 예제 2: *args, **kwargs를 사용하는 데코레이터

import time

def elapsed(original_func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = original_func(*args, **kwargs)
        end = time.time()
        print("함수 수행시간: %f 초" % (end - start))
        return result
    return wrapper

@elapsed
def myfunc(msg):
    print("'%s'을 출력합니다." % msg)

myfunc("You need python")

*args**kwargs는 데코레이터 함수가 기존 함수의 모든 인자를 처리할 수 있도록 해준다. 이렇게 하면 함수의 인자에 상관없이 데코레이터를 적용할 수 있다.

정리

  • 클로저(Closure): 함수 내부에 정의된 함수가 외부 함수의 변수를 기억하고 사용할 수 있는 구조이다. 함수의 재사용성을 높여준다.
  • 데코레이터(Decorator): 기존 함수에 새로운 기능을 추가하는 방법이다. 기존 코드를 수정하지 않고도 함수를 꾸며줄 수 있다.

이 두 개념을 활용하면 코드의 모듈화와 재사용성이 높아지고, 함수의 기능을 더욱 유연하게 확장할 수 있다.


🏖️ 함수 재귀호출

재귀호출(Recursive Call)이란, 함수 내부에서 자신을 다시 호출하는 방식을 말한다. 일반적인 상황에서는 잘 사용하지 않지만, 알고리즘을 구현할 때 매우 유용하게 쓰인다. 특정 알고리즘에 따라 반복문보다 재귀호출로 구현한 코드가 더 직관적이고 이해하기 쉬운 경우가 많다.

재귀호출 사용하기

먼저 간단한 재귀호출 함수의 예시를 보자. 다음 코드를 IDLE의 소스 코드 편집 창에 입력하고 실행해보자.

def hello():
    print('Hello, world!')
    hello()
 
hello()

실행 결과:

Hello, world!
Hello, world!
Hello, world!
...(생략)
Traceback (most recent call last):
  File "C:\project\recursive_function_error.py", line 5, in <module>
    hello()
  ...
RecursionError: maximum recursion depth exceeded while pickling an object
  • hello 함수가 자기 자신을 계속 호출하고 있다. 실행 결과 'Hello, world!' 문자열이 반복적으로 출력되다가 RecursionError가 발생한다.
  • 파이썬에서 최대 재귀 깊이(maximum recursion depth)가 1,000으로 제한되어 있기 때문이다. 함수가 자기 자신을 반복해서 호출하다가 이 깊이를 초과하면 에러가 발생하게 된다.
  • 함수가 계속해서 스택(stack)에 쌓이다가 스택이 넘쳐서 에러가 발생하는 모양을 보인다.

재귀호출에 종료 조건 만들기

재귀호출을 사용할 때는 반드시 종료 조건을 만들어야 한다. 종료 조건 없이 계속해서 자기 자신을 호출하면 무한 루프에 빠지고, 결국 스택 오버플로우(stack overflow)로 인해 프로그램이 종료된다.

def hello(count):
    if count == 0:    # 종료 조건: count가 0이면 함수 호출을 멈춤
        return
    
    print('Hello, world!', count)
    
    count -= 1      # count를 1 감소시킴
    hello(count)    # 감소된 count로 다시 hello 함수 호출
 
hello(5)    # hello 함수 호출

실행 결과:

Hello, world! 5
Hello, world! 4
Hello, world! 3
Hello, world! 2
Hello, world! 1

이 코드에서는 hello 함수가 매개변수 count를 받아서, count가 0이 될 때까지 자신을 호출한다. count가 0이 되면 더 이상 함수 호출을 하지 않고 종료된다. count가 0이 아니면 'Hello, world!'와 현재 count 값을 출력하고, count를 1 감소시킨 후 다시 hello 함수를 호출한다.

이와 같이, 재귀호출에서는 종료 조건을 명확히 설정하여 무한 호출을 방지해야 한다.

profile
AI Engineer / 의료인공지능

0개의 댓글