파이썬의 데코레이터

파이썬의 데코레이터

이 문서는 파이썬의 데코레이터에 대해 설명합니다.

YouTube Video

파이썬의 데코레이터

파이썬 데코레이터는 함수나 메서드에 추가적인 기능을 더할 수 있는 강력한 기능입니다. 데코레이터를 사용하면 기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있어, 코드 재사용성과 유지보수성을 향상시킬 수 있습니다.

데코레이터의 기초

파이썬 데코레이터는 함수를 인수로 받아 추가 기능이 있는 새 함수를 반환하는 방식으로 동작합니다. 데코레이터를 사용하면 함수에 전처리나 후처리를 간단히 추가할 수 있습니다.

데코레이터를 사용하지 않으면 함수의 동작을 변경하려면 함수 자체를 직접 수정해야 합니다. 데코레이터를 사용하면 원래 구현을 수정하지 않고도 기능을 확장할 수 있습니다.

데코레이터의 기본 구조

 1def my_decorator(func):
 2    def wrapper():
 3        print("Before the function call")
 4        func()
 5        print("After the function call")
 6    return wrapper
 7
 8@my_decorator
 9def say_hello():
10    print("Hello!")
11
12say_hello()

이 예제에서 my_decorator 함수는 데코레이터 역할을 하여 say_hello 함수를 감쌉니다. @my_decorator 문법을 사용하여 데코레이터를 적용하며, say_hello()가 호출되면 데코레이터에서 제공하는 전처리와 후처리가 자동으로 실행됩니다.

데코레이터 작동 방식

데코레이터는 다음 단계에서 작동합니다.

  1. 데코레이터는 함수(또는 메서드)를 인수로 받습니다.

  2. 원래 함수를 실행하기 위한 래퍼 함수를 정의합니다.

  3. 래퍼 함수는 원래 함수를 실행하기 전 또는 후에 추가 처리를 수행합니다.

  4. 데코레이터는 래퍼 함수를 반환합니다. 결과적으로 원래 함수는 새로 데코레이트된 함수로 대체됩니다.

인수를 가진 함수의 데코레이터

인수를 가진 함수에 데코레이터를 적용할 때 래퍼 함수는 해당 인수를 받을 수 있도록 조정되어야 합니다.

 1def my_decorator(func):
 2    def wrapper(*args, **kwargs):
 3        print("Before the function call")
 4        result = func(*args, **kwargs)
 5        print("After the function call")
 6        return result
 7    return wrapper
 8
 9@my_decorator
10def greet(name):
11    print(f"Hello, {name}!")
12
13greet("Alice")

*args**kwargs를 사용하면 임의의 개수의 인수와 키워드 인수를 수용하는 함수를 처리할 수 있습니다. 이를 통해 데코레이터를 임의의 유형의 인수를 가진 함수에 일반적으로 적용할 수 있습니다.

데코레이터 적용 예시

로그 기록용 데코레이터

데코레이터는 종종 로그 기능을 추가하는 데 사용됩니다. 예를 들어, 함수 실행 전후를 로그로 기록하는 데코레이터를 작성함으로써 함수가 호출된 시점과 실행에 걸리는 시간을 기록할 수 있습니다.

 1import time
 2
 3def log_time(func):
 4    def wrapper(*args, **kwargs):
 5        start_time = time.time()
 6        print(f"Calling function '{func.__name__}'...")
 7        result = func(*args, **kwargs)
 8        end_time = time.time()
 9        print(f"Function '{func.__name__}' completed in {end_time - start_time} seconds")
10        return result
11    return wrapper
12
13@log_time
14def long_task(duration):
15    time.sleep(duration)
16    print("Task completed!")
17
18long_task(2)

이 예에서는 log_time 데코레이터가 함수의 실행 시간을 측정하고 이를 로그로 출력합니다. long_task 함수는 데코레이터로 감싸져 있으며, 실행 시간은 런타임 동안 기록됩니다.

권한 관리를 위한 데코레이터

데코레이터는 권한 관리에도 사용할 수 있습니다. 예를 들어, 사용자가 특정 권한을 가지고 있는지 확인하여 처리를 제한할 수 있습니다.

 1def requires_permission(user_role):
 2    def decorator(func):
 3        def wrapper(*args, **kwargs):
 4            if user_role != "admin":
 5                print("Permission denied!")
 6                return
 7            return func(*args, **kwargs)
 8        return wrapper
 9    return decorator
10
11@requires_permission("admin")
12def delete_user(user_id):
13    print(f"User {user_id} has been deleted.")
14
15delete_user(123)  # Executed
16delete_user = requires_permission("guest")(delete_user)
17delete_user(456)  # Permission denied!

requires_permission 데코레이터는 사용자의 역할에 따라 함수 실행을 제한합니다. 이러한 로직을 데코레이터에 통합함으로써 권한 관리 로직이 코드 전체에 분산되는 것을 방지하고 가독성을 높일 수 있습니다.

데코레이터 중첩

여러 데코레이터를 적용하는 것이 가능합니다. 여러 데코레이터가 단일 함수에 적용되었을 경우, 위에서 아래 순서대로 실행됩니다.

 1def uppercase(func):
 2    def wrapper(*args, **kwargs):
 3        result = func(*args, **kwargs)
 4        return result.upper()
 5    return wrapper
 6
 7def exclaim(func):
 8    def wrapper(*args, **kwargs):
 9        result = func(*args, **kwargs)
10        return result + "!"
11    return wrapper
12
13@uppercase
14@exclaim
15def greet(name):
16    return f"Hello, {name}"
17
18print(greet("Alice"))  # "HELLO, ALICE!"

이 예제에서는 두 개의 데코레이터가 greet 함수에 적용됩니다. @exclaim 데코레이터는 문자열 끝에 느낌표를 추가하고, @uppercase는 문자열을 대문자로 변환합니다.

클래스 메서드를 위한 데코레이터

데코레이터는 클래스 메서드에서도 사용할 수 있습니다. 이는 클래스 내 메서드의 동작을 제어하고자 할 때 특히 유용합니다. 클래스 메서드에 데코레이터를 적용할 때 self 또는 cls와 같은 인수에 유의해야 합니다.

 1def log_method_call(func):
 2    def wrapper(self, *args, **kwargs):
 3        print(f"Calling method '{func.__name__}'...")
 4        return func(self, *args, **kwargs)
 5    return wrapper
 6
 7class MyClass:
 8    @log_method_call
 9    def greet(self, name):
10        print(f"Hello, {name}")
11
12obj = MyClass()
13obj.greet("Bob")

이 예제에서는 log_method_call 데코레이터가 greet 메서드에 적용되어 메서드 호출 시 로그를 출력합니다.

데코레이터 사용 시 주의사항

데코레이터 사용 시 고려해야 할 사항이 있습니다. 데코레이터가 원래 함수의 이름 및 문서화 문자열(e.g., __name__ 또는 __doc__)을 변경할 수 있으므로, functools.wraps를 사용하여 이러한 정보를 보존하는 것이 좋습니다.

1import functools
2
3def my_decorator(func):
4    @functools.wraps(func)
5    def wrapper(*args, **kwargs):
6        print("Before the function call")
7        return func(*args, **kwargs)
8    return wrapper

@functools.wraps를 사용하면 원래 함수의 메타데이터가 래퍼 함수로 올바르게 전달되도록 보장됩니다.

요약

Python 데코레이터는 함수와 메서드에 추가 기능을 간결하게 추가할 수 있는 매우 강력한 도구입니다. 데코레이터는 코드 중복을 줄이고 유지보수성과 재사용성을 향상시키기 위해 다양한 상황에서 사용할 수 있습니다. 데코레이터의 작동 방식을 이해하면 더 효율적이고 유연한 코드를 작성할 수 있습니다.

위의 기사를 보면서 Visual Studio Code를 사용해 우리 유튜브 채널에서 함께 따라할 수 있습니다. 유튜브 채널도 확인해 주세요.

YouTube Video