파이썬에서 클래스에 데코레이터를 적용하면 클래스의 동작을 동적으로 수정하거나 확장할 수 있습니다. 다음은 클래스 데코레이터를 구현하는 여러 가지 방법과 예제입니다.
1. 클래스 직접 수정하기
데코레이터는 클래스 자체를 수정하여 메서드나 속성을 추가할 수 있습니다.
메서드 추가 예제
def attach_welcome_message(target_class):
def welcome(self):
print(f"{self.__class__.__name__} 인사말입니다!")
target_class.welcome = welcome
return target_class
@attach_welcome_message
class User:
pass
u = User()
u.welcome() # 출력: User 인사말입니다!
속성 추가 예제
def set_metadata(**attributes):
def wrapper(cls):
for key, value in attributes.items():
setattr(cls, key, value)
return cls
return wrapper
@set_metadata(release="v1.0", author="Alice")
class Application:
pass
print(Application.release) # 출력: v1.0
print(Application.author) # 출력: Alice
2. 서브클래싱으로 확장하기
원본 클래스를 변경하지 않고 새로운 기능을 추가하려면 상속을 활용할 수 있습니다.
인스턴스 생성 로깅 예제
def log_instance_creation(original_class):
class EnhancedClass(original_class):
def __init__(self, *args, **kwargs):
print(f"{original_class.__name__} 객체 생성됨 - args: {args}")
super().__init__(*args, **kwargs)
return EnhancedClass
@log_instance_creation
class DataProcessor:
def __init__(self, data):
self.data = data
processor = DataProcessor([1, 2, 3]) # 출력: DataProcessor 객체 생성됨 - args: ([1, 2, 3],)
print(processor.data) # 출력: [1, 2, 3]
3. 매개변수화된 데코레이터
데코레이터에 매개변수를 전달하여 클래스 동작을 조정할 수 있습니다.
여러 속성 일괄 추가 예제
def inject_attributes(**fields):
def decorator(cls):
for name, value in fields.items():
setattr(cls, name, value)
return cls
return decorator
@inject_attributes(version="3.0", developer="Bob")
class Module:
pass
print(Module.version) # 출력: 3.0
print(Module.developer) # 출력: Bob
4. 싱글톤 패턴 구현
클래스가 오직 하나의 인스턴스만 갖도록 제한하는 데코레이터를 만들 수 있습니다.
__new__ 메서드 재정의 방식
def enforce_singleton(cls):
instance_ref = [None]
original_new = cls.__new__
def custom_new(subclass, *args, **kwargs):
if instance_ref[0] is None:
instance_ref[0] = original_new(subclass) if original_new != object.__new__ else original_new(subclass)
return instance_ref[0]
cls.__new__ = staticmethod(custom_new)
cls.__init__ = lambda self, *a, **kw: None # 초기화 중복 방지
return cls
@enforce_singleton
class UniqueService:
def __init__(self, identifier):
self.identifier = identifier
first = UniqueService("ID1")
second = UniqueService("ID2")
print(first is second) # 출력: True
# print(first.identifier) # AttributeError 발생 (초기화 무시됨)
5. 클래스 등록 시스템
전역 저장소에 클래스를 자동 등록하여 나중에 접근할 수 있도록 합니다.
딕셔너리에 클래스 등록 예제
registered_classes = {}
def register_class(alias):
def decorator(cls):
registered_classes[alias] = cls
return cls
return decorator
@register_class('data_handler')
class Handler:
pass
print(registered_classes['data_handler']) # 출력: <class '__main__.Handler'>
주의사항
- 부작용 관리: 클래스를 직접 수정하면 다른 코드에 영향을 줄 수 있으므로, 가능하면 상속을 사용하세요.
- 생성자 호환성: 상속 기반 확장을 할 때 부모 클래스의
__init__메서드와 인수를 잘 맞춰야 합니다. - 메타클래스 충돌: 이미 메타클래스를 사용 중인 클래스에는 특별히 주의해야 합니다.
적절하게 설계된 클래스 데코레이터는 원본 소스 코드를 변경하지 않고도 클래스의 기능을 유연하게 확장할 수 있게 해줍니다. 이는 개방-폐쇄 원칙(OCP)을 따르는 좋은 예시입니다.