Menu
Coddy logo textTech

클래스 데코레이터

Coddy Python 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 64개 중 40번째.

class 데코레이터를 사용하면 다른 function으로 감싸 클래스를 수정하거나 향상할 수 있습니다. function 데코레이터와 같은 방식으로 작동하지만 전체 클래스에 적용됩니다.

다음은 데코레이션이 없는 간단한 클래스입니다:

class Person:
    def __init__(self, name):
        self.name = name
        
    def greet(self):
        return f"Hello, my name is {self.name}"

새로운 메서드를 추가하는 클래스 데코레이터를 만드세요:

def add_farewell(cls):
    def farewell(self):
        return f"Goodbye from {self.name}"
    
    cls.farewell = farewell  # 클래스에 메서드를 추가
    return cls

@를 사용하여 decorator를 class에 적용하세요:

@add_farewell
class EnhancedPerson:
    def __init__(self, name):
        self.name = name
        
    def greet(self):
        return f"Hello, my name is {self.name}"

이제 class에는 both original과 추가된 methods가 있습니다:

person = EnhancedPerson("Alice")
print(person.greet())     # 안녕하세요, 제 이름은 Alice입니다
print(person.farewell())  # Alice로부터 안녕히 가세요

기존 method를 래핑할 수도 있습니다. original을 저장해 두면 wrapper 내부에서 여전히 호출할 수 있습니다. 이는 클래스가 이미 가지고 있는 method 주변에 behaviour(추적이나 로깅 등)를 추가하려는 경우 유용합니다:

def add_tracking(cls):
    original_greet = cls.greet  # 원본 메서드를 저장

    def tracked_greet(self):
        print(f"greet was called")   # 호출 전 추가 동작
        return original_greet(self)  # 원본 메서드 호출

    cls.greet = tracked_greet  # 클래스의 메서드를 교체
    return cls

@add_tracking
class TrackedPerson:
    def __init__(self, name):
        self.name = name

    def greet(self):
        return f"Hello, my name is {self.name}"

이제 greet을 호출하면 래퍼가 먼저 실행된 다음 원래 함수에 위임합니다.

person = TrackedPerson("Alice")
print(person.greet())
# greet가 호출되었습니다
# 안녕하세요, 제 이름은 Alice입니다

기존 method를 래핑하는 주요 단계는 다음과 같습니다:
1. original method를 저장합니다: original = cls.method
2. original(self)을 호출하고 추가 로직을 포함하는 새로운 함수를 Define합니다.
3. 새로운 함수를 class에 다시 할당합니다: cls.method = new_function
4. modified class를 Return합니다.

핵심 요점: class decorator는 class를 input으로 받아 수정하거나 향상시키고, modified class를 반환합니다. methods를 추가하거나 속성을 수정하거나 기존 functionality를 래핑할 수 있습니다. class definition 위에 @decorator_name을 사용하여 적용합니다. 이를 통해 상속 없이도 class functionality를 확장하는 깔끔한 방법을 제공합니다.

challenge icon

챌린지

중급

이 과제에서는 메서드 호출 추적 기능을 추가하는 클래스 데코레이터를 구현합니다.

  • decorator.py - add_counter 클래스 데코레이터를 구현합니다
  • calculator.py - 데코레이터를 사용할 클래스가 포함되어 있습니다

driver.py 파일에는 다양한 사용 사례, 상속 패턴 및 예외적인 경우에 대해 구현을 검증하는 광범위한 테스트 시나리오가 포함되어 있습니다. 이 파일을 수정할 필요는 없지만, 살펴보면 예상되는 동작을 이해하는 데 도움이 됩니다.

직접 해보기

from calculator import Calculator

# 포괄적인 테스트 케이스 핸들러
test_case = input()

if test_case == "basic_functionality":
    calc = Calculator()
    print(calc.add(5, 3))  # 8을 출력해야 함
    print(calc.add(2, 7))  # 9를 출력해야 함
    print(calc.subtract(10, 4))  # 6을 출력해야 함
    print(calc.call_counts)  # Should print {'add': 2, 'subtract': 1}

elif test_case == "multiple_instances":
    calc1 = Calculator()
    calc2 = Calculator()
    
    print(calc1.add(5, 3))  # 8을 출력해야 함
    print(calc2.add(2, 7))  # 9를 출력해야 함
    print(calc1.subtract(10, 4))  # 6을 출력해야 함
    
    # 두 인스턴스는 동일한 call_counts를 공유해야 함
    print(calc1.call_counts)  # Should print {'add': 2, 'subtract': 1}
    print(calc2.call_counts)  # Should print {'add': 2, 'subtract': 1}
    print(calc1.call_counts is calc2.call_counts)  # True를 출력해야 함

elif test_case == "method_call_order":
    calc = Calculator()
    
    print(calc.add(1, 2))  # 3을 출력해야 함
    print(calc.subtract(5, 2))  # 3을 출력해야 함
    print(calc.add(10, 20))  # 30을 출력해야 함
    print(calc.add(7, 3))  # 10을 출력해야 함
    print(calc.call_counts)  # Should print {'add': 3, 'subtract': 1}

elif test_case == "large_values":
    calc = Calculator()
    
    print(calc.add(1000000, 2000000))  # 3000000을 출력해야 함
    print(calc.subtract(5000000, 2000000))  # 3000000을 출력해야 함
    print(calc.call_counts)  # Should print {'add': 1, 'subtract': 1}

elif test_case == "negative_values":
    calc = Calculator()
    
    print(calc.add(-5, -3))  # -8을 출력해야 함
    print(calc.subtract(-10, -4))  # -6을 출력해야 함
    print(calc.call_counts)  # Should print {'add': 1, 'subtract': 1}

elif test_case == "float_values":
    calc = Calculator()
    
    print(calc.add(5.5, 3.2))  # 8.7을 출력해야 함
    print(calc.subtract(10.5, 4.2))  # 6.3을 출력해야 함
    print(calc.call_counts)  # Should print {'add': 1, 'subtract': 1}

elif test_case == "zero_values":
    calc = Calculator()
    
    print(calc.add(0, 0))  # 0을 출력해야 함
    print(calc.subtract(0, 0))  # 0을 출력해야 함
    print(calc.call_counts)  # Should print {'add': 1, 'subtract': 1}

elif test_case == "counter_reset":
    calc = Calculator()
    
    print(calc.add(5, 3))  # 8을 출력해야 함
    print(calc.call_counts)  # Should print {'add': 1, 'subtract': 0}
    
    # 카운터 재설정
    calc.call_counts = {"add": 0, "subtract": 0}
    
    print(calc.add(2, 7))  # 9를 출력해야 함
    print(calc.call_counts)  # Should print {'add': 0, 'subtract': 0}

elif test_case == "counter_manipulation":
    calc = Calculator()
    
    print(calc.add(5, 3))  # 8을 출력해야 함
    print(calc.call_counts)  # Should print {'add': 1, 'subtract': 0}
    
    # 카운터를 직접 조작
    calc.call_counts["add"] = 100
    
    print(calc.add(2, 7))  # 9를 출력해야 함
    print(calc.call_counts)  # Should print {'add': 101, 'subtract': 0}

elif test_case == "performance_test":
    calc = Calculator()
    
    # 1000번의 add 연산 수행
    for i in range(1000):
        calc.add(i, i+1)
    
    # 500번의 subtract 연산 수행
    for i in range(500):
        calc.subtract(i+10, i)
    
    print(calc.call_counts)  # Should print {'add': 1000, 'subtract': 500}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 Python 컴파일러