본문 바로가기
코딩취미/Python

[Python] 데코레이터의 종류와 특징, 사용 방법 정리

by 브링블링 2025. 2. 25.
반응형

📌 [Python] 데코레이터의 종류와 특징, 사용 방법 정리


📝 소개

Python의 **데코레이터(Decorator)**는 함수 또는 클래스를 수정하지 않고 추가적인 기능을 부여하는 강력한 도구입니다.
쉽게 말해, 기존 코드를 변경하지 않고 기능을 확장할 수 있도록 도와주는 것입니다.

이번 글에서는 데코레이터의 개념, 종류, 사용법을 초보자도 이해할 수 있도록 쉽게 정리하겠습니다.
또한, 실제 프로젝트에서 활용할 수 있는 예제 코드를 함께 제공하겠습니다.


🔍 데코레이터(Decorator)란?

✅ 데코레이터의 정의

  • 데코레이터는 기존 함수나 클래스를 감싸서 새로운 기능을 추가하는 Python 기능
  • @데코레이터_이름 형태로 사용
  • 코드 수정 없이 기능 확장 가능

✅ 데코레이터의 특징

  1. 함수를 인자로 받아 실행
    • 데코레이터는 다른 함수를 감싸는 함수입니다.
  2. 원래 함수의 기능을 변경하지 않고 확장 가능
    • 함수 코드 수정 없이 로깅, 권한 체크, 실행 시간 측정 등의 기능 추가 가능
  3. 가독성 향상
    • 코드가 깔끔해지고 중복을 줄일 수 있음

📌 데코레이터 기본 구조

def my_decorator(func):
    def wrapper():
        print("Before function execution")
        func()  # 원래 함수 실행
        print("After function execution")
    return wrapper  # 감싸진 함수 반환

@my_decorator  # 데코레이터 적용
def hello():
    print("Hello, World!")

hello()

🔹 실행 결과

Before function execution
Hello, World!
After function execution

 

위 코드에서 @my_decorator를 사용하면 hello() 함수가 my_decorator의 wrapper 함수 안에서 실행됩니다.

반응형

🎯 데코레이터의 종류와 특징

Python에서 사용되는 대표적인 데코레이터의 종류를 정리하면 다음과 같습니다.

종류 설명 사용 예제
기본 함수 데코레이터 가장 기본적인 형태의 데코레이터 @my_decorator
인자를 받는 데코레이터 데코레이터에 추가적인 설정 값을 전달 @log("DEBUG")
functools.wraps 적용 데코레이터 원본 함수의 __name__과 __doc__을 유지 @functools.wraps(func)
클래스 데코레이터 클래스를 감싸는 데코레이터 @my_class_decorator
메서드 데코레이터 (@staticmethod, @classmethod) 클래스 내부 메서드에 사용되는 데코레이터 @staticmethod, @classmethod
내장 데코레이터 (@property) 클래스 속성을 메서드처럼 사용 가능 @property
PySide6의 @Slot 데코레이터 Signal과 연결되는 메서드를 최적화 @Slot()

📌 다양한 데코레이터 예제

✅ 1. 기본 함수 데코레이터

def simple_decorator(func):
    def wrapper():
        print("Function is about to execute")
        func()
        print("Function execution completed")
    return wrapper

@simple_decorator
def say_hello():
    print("Hello!")

say_hello()

✅ 2. 인자를 받는 데코레이터

 
def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(3)
def greet():
    print("Hello!")

greet()

✅ 3. functools.wraps를 활용한 데코레이터

import functools

def log_function(func):
    @functools.wraps(func)  # 원본 함수 정보 유지
    def wrapper(*args, **kwargs):
        print(f"Executing {func.__name__} function")
        return func(*args, **kwargs)
    return wrapper

@log_function
def add(a, b):
    """두 수를 더하는 함수"""
    return a + b

print(add(2, 3))
print(add.__name__)  # 원본 함수 이름 유지

 

🔹 functools.wraps(func)를 사용하면 함수 이름(__name__)과 설명(__doc__)이 유지됩니다.


✅ 4. 클래스 데코레이터

class UpperCaseDecorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        result = self.func(*args, **kwargs)
        return result.upper()

@UpperCaseDecorator
def get_text():
    return "hello, world!"

print(get_text())  # "HELLO, WORLD!"

 

🔹 클래스를 활용한 데코레이터는 __call__ 메서드를 사용하여 함수처럼 동작할 수 있습니다.


✅ 5. @staticmethod, @classmethod 사용 예제

class Math:
    @staticmethod
    def add(x, y):
        return x + y

    @classmethod
    def multiply(cls, x, y):
        return x * y

print(Math.add(2, 3))  # 5
print(Math.multiply(2, 3))  # 6

✅ 6. @property 사용 예제

class Person:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

person = Person("Alice")
print(person.name)  # "Alice"

 

🔹 @property를 사용하면 메서드를 속성처럼 사용할 수 있습니다.

 


📌 PySide6에서 @Slot 데코레이터 사용하기

✅ @Slot 데코레이터를 사용하는 이유

PySide6에서 @Slot 데코레이터를 사용하면 다음과 같은 장점이 있습니다.

  1. Signal과 연결될 메서드를 명확히 표시
  2. 성능 최적화
    • C++에서 직접 호출할 수 있도록 최적화됨
  3. 타입 안정성 제공
    • 인자의 타입을 명시할 수 있어 오류 방지 가능

✅ @Slot 사용 예제

from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QVBoxLayout
from PySide6.QtCore import Signal, Slot

class MyWindow(QWidget):
    button_clicked = Signal()  # 버튼 클릭을 위한 Signal 정의

    def __init__(self):
        super().__init__()
        self.setWindowTitle("Signal & Slot Example")
        self.resize(300, 200)

        # UI 요소 생성
        self.label = QLabel("Press the button!")
        self.button = QPushButton("Click Me")
        self.button.clicked.connect(self.emit_signal)

        # Signal-Slot 연결
        self.button_clicked.connect(self.update_label)

        # 레이아웃 설정
        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.button)
        self.setLayout(layout)

    def emit_signal(self):
        """Signal을 발생시키는 메서드"""
        self.button_clicked.emit()

    @Slot()  # Slot 데코레이터 사용
    def update_label(self):
        """Label의 텍스트를 변경하는 Slot 메서드"""
        self.label.setText("Button Clicked!")

# 앱 실행
if __name__ == "__main__":
    app = QApplication([])
    window = MyWindow()
    window.show()
    app.exec()

🔹 실행 결과

  1. 초기 상태: "Press the button!"
  2. 버튼 클릭 → Signal 발생 → Slot 실행
  3. 텍스트 변경: "Button Clicked!"

🎯 @Slot을 사용한 경우 vs 사용하지 않은 경우

PySide6에서 @Slot 데코레이터는 선택적으로 사용할 수 있습니다.
하지만, 데코레이터를 사용하면 최적화된 호출 경로를 제공하고, 데이터 타입을 강제할 수 있다는 장점이 있습니다.

🔹 @Slot을 사용한 경우

from PySide6.QtCore import Slot

class Example:
    @Slot()  # Slot 데코레이터 적용
    def my_slot(self):
        print("Slot is called!")

🔹 @Slot을 사용하지 않은 경우

class Example:
    def my_slot(self):
        print("Slot is called!")

✅ 차이점 정리

특성 Slot 데코레이터 사용 Slot 데코레이터 미사용
명시적 등록 Slot임을 명확히 선언 일반적인 Python 함수
성능 최적화 Qt 내부적으로 최적화됨 Python 함수로 실행됨
타입 안정성 데이터 타입을 명시할 수 있음 타입 안정성이 없음
유지보수성 Signal-Slot 메커니즘을 명확히 표현 Slot 여부가 코드에서 명확하지 않음

🚀 정리

  • 데코레이터는 함수 또는 클래스를 감싸서 기능을 추가하는 강력한 도구
  • 기본적인 함수 데코레이터부터 클래스, 메서드, 내장 데코레이터까지 다양하게 활용 가능
  • functools.wraps를 활용하면 원본 함수의 정보를 유지할 수 있음
  • 클래스 기반 데코레이터를 활용하면 더 확장성이 높아짐
반응형