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

보통 클래스 검증 코드는 클래스의 객체가 생성될 때 __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 문의 본문 전체가 처리된 후에 실행


+ Random Posts