'Better way 51. 합성 가능한 클래스 확장이 필요하면 메타클래스보다는 클래스 데코레이터를 사용하라' 정리

Effective Python 2nd 파이썬 코딩의 기술 (교보문고 링크)을 제대로 이해하고자 블로그에 정리합니다.



현재 위치

Note
<6. Metaclasses and Attributes> Item 51: Prefer Class Decorators Over Metaclasses for Compoable Class Extensions Better Way 51. 합성 가능한 클래스 확장이 필요하면 메타클래스보다는 클래스 데코레이터를 사용하라



클래스 데코레이터를 사용하면 클래스를 쉽게 확장할 수 있습니다.



어떤 클래스의 모든 메서드에 대해서 호출 결과를 출력하고 싶은 상황입니다. ​ ‘Better way 26. functools.wrap을 사용해 함수 데코레이터를 정의하라’ 정리와 비슷하게 trace_func()을 적용하고 싶습니다. ​

import types
from functools import wraps


# Example 1
def trace_func(func):
    if hasattr(func, 'tracing'):  # Only decorate once
        return func

    @wraps(func)
    def wrapper(*args, **kwargs):
        result = None
        try:
            result = func(*args, **kwargs)
            return result
        except Exception as e:
            result = e
            raise
        finally:
            print(f'{func.__name__}({args!r}, {kwargs!r}) -> {result!r}')  # 요걸 출력하고 싶음

    wrapper.tracing = True
    return wrapper

​ 클래스 데코레이터를 적용한 예제입니다. ​

# Example 4
trace_types = (
    types.MethodType,
    types.FunctionType,
    types.BuiltinFunctionType,
    types.BuiltinMethodType,
    types.MethodDescriptorType,
    types.ClassMethodDescriptorType)


# Example 9 (데코레이터 함수: 인자로 받은 클래스를 적절히 변경해서 재생성함)
def trace(klass):
    for key in dir(klass):
        value = getattr(klass, key)
        if isinstance(value, trace_types):
            wrapped = trace_func(value)
            setattr(klass, key, wrapped)
    return klass


# Example 10
@trace  # TraceDict 클래스에 @trace 클래스 데코레이터 적용
class TraceDict(dict):
    pass

​ TraceDict 클래스 사용 부분 입니다. ​

trace_dict = TraceDict([('hi', 1)])
trace_dict['there'] = 2
temp = trace_dict['hi']
try:
    temp = trace_dict['does not exist']
except KeyError:
    pass  # Expected
else:
    assert False

​ 실행 결과 입니다. ​

__new__((<class '__main__.TraceDict'>, [('hi', 1)]), {}) -> {}
__getitem__(({'hi': 1, 'there': 2}, 'hi'), {}) -> 1
__getitem__(({'hi': 1, 'there': 2}, 'does not exist'), {}) -> KeyError('does not exist')


책에서 챕터 마지막 부분에 적혀있는 내용입니다.

Tip
A class decorator is a simple function that receives a class instance as a parameter and returns either a new class or a modified version of the original class. 클래스 데코레이터는 class 인스턴스를 파라미터로 받아서 이 클래스를 변경한 클래스나 새로운 클래스를 반환해주는 간단한 함수다.
Tip
Class decorators are useful when you want to modify every method or attribute of a class with minimal boilerplate. 준비 코드를 최소화하면서 클래스 내부의 모든 메서드나 애트리뷰트를 변경하고 싶을 때 클래스 데코레이터가 유용하다.
Tip
Metaclasses can’t be composed together easily, while many class decorators can be used to extend the same class without conflicts. 메타클래스는 서로 쉽게 합성할 수 없지만, 여러 클래스 데코레이터를 충돌 없이 사용해 똑같은 클래스를 확장할 수 있다.




Related Content