ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] Tip - 메타클래스로 서브클래스를 검증
    언어/파이썬 & 장고 2016. 11. 26. 17:48

    메타클래스를 응용하는 가장 간단한 사례는 클래스를 올바르게 정의했는지 검증하는 것입니다. 복잡한 클래스 계층을 만들 때 스타일을 강제하거나 메서드를 오버라이드하도록 요구하거나 클래스 속성 사이에 철저한 관계를 두고 싶을 수도 있습니다. 메타클래스는 서브클래스가 정의될 때마다 검증 코드를 실행하는 신뢰할 만한 방법을 제공하므로 이럴 때 사용할 수 있습니다.

    보통 클래스 검증 코드는 클래스의 객체가 생성될 때 __init__ 메서드에서 실행됩니다. 메타클래스를 검증용으로 사용하면 오류를 더 빨리 일으킬 수 있습니다.

    서브클래스 검증용으로 메타클래스를 정의하는 방법을 알아보기에 앞서 메타클래스가 표준 객체에는 어떻게 동작하는지 이해해야 합니다. 메타클래스는 type을 상속하여 정의합니다. 메타클래스는 기본으로 자체의 __new__ 메서드에서 연관된 class 문의 콘텐츠를 받습니다. 여기서 타입이 실제로 생성되기 전에 클래스 정보를 수집할 수 있습니다.


    class Meta(type):
        def __new__(meta, name, bases, class_dict):
            print((meta, name, bases, class_dict))
    		# 메타, 클래스이름, 클래스 기본 타입(?), 해당 클래스 속성(변수,함수, 클래스 이름 등 dict 타입으로)
            return type.__new__(meta, name, bases, class_dict)
    
    
    class MyClass(object, metaclass=Meta):
        stuff = 123
    
        def foo(self):
            pass
    
    # 결과
    # (<class '__main__.Meta'>, 'MyClass', (<class 'object'>,), {'foo': <function MyClass.foo at 0x1007acd08>, '__module__': '__main__', '__qualname__': 'MyClass', 'stuff': 123})
    
    

    메타클래스는 클래스의 이름, 클래스가 상속하는 부모 클래스, class 본문에서 정의한 모든 클래스 속성에 접근할 수 있습니다.


    클래스가 정의되기 전에 클래스의 모든 파라미터를 검증하려면 Meta.__new__ 메서드에 기능을 추가하면 됩니다. 예를 들어 여러 면으로 이루어진 다각형을 어떤 타입이든 표현하고 싶다고 가정합니다. 이렇게 하려면 특별한 검증용 메타클래스를 정의한 후 다각형 클래스 계층의 기반 클래스에 사용하면 됩니다. 이때 기반 클래스에는 같은 검증을 적용하지 말아야 한다는 점을 유의해야 합니다.

    class ValidatePolygon(type):
        def __new__(meta, name, bases, class_dict):
            # 추상 Polygon 클래스는 검증하지 않음
            if bases != (object,):
                if class_dict['sides'] < 3:
                    raise ValueError('Polygons need 3+ sides')
            return type.__new__(meta, name, bases, class_dict)
    
    
    class Polygon(object, metaclass=ValidatePolygon):
        sides = None  # 서브클래스에서 설정함
    
        @classmethod
        def interior_angles(cls):
            return (cls.sides - 2) * 100
    
    
    class Triangle(Polygon):
        sides = 3
    
    
    print('Before class')
    
    
    class Line(Polygon):
        print('Before sides')
        sides = 1 # 각이 1개 이하여서 에러 발생
        print('After sides')
    
    
    print('After class')
    
    # 결과
    # Before class
    # Traceback (most recent call last):
    # Before sides
    # After sides
    # ValueError: Polygons need 3+ sides


    위 코드에서 ValidatePolygon 클래스가 받는 파라미터들은 다음과 같습니다.

    # Polygon 클래스
    parameters:  
    name:  Polygon 
    bases:  (<class 'object'>,) 
    class_dict:  {'__module__': '__main__', '__qualname__': 'Polygon', 'interior_angles': <classmethod object at 0x1028ae128>, 'sides': None}
    
    
    # Triangle 클래스
    parameters:  
    name:  Triangle 
    bases:  (<class '__main__.Polygon'>,) 
    class_dict:  {'__module__': '__main__', '__qualname__': 'Triangle', 'sides': 3}

    요약

    서브클래스 타입의 객체를 생성하기에 앞서 서브클래스가 정의 시점부터 제대로 구성되었음을 보장하려면 메타클래스를 사용

    메타클래스의 __new__ 메서드는 class 문의 본문 전체가 처리된 후에 실행


    댓글