본문 바로가기
Programming Language/Python

Effective Python. 메타클래스로 서브클래스를 검증하자.

by 알파해커 2021. 3. 3.
반응형

메타클래스를 응용하는 가장 간단한 사례는 클래스를 올바르게 정의했는지 검증하는 것이다.

클래스 계층을 만들 때 스타일을 강제하거나, 메서드를 오버라이드하도록 요구하거나, 클래스 속성 사이에 관계를 두고 싶을 수도 있다.

 

보통 클래스 검증 코드는 클래스의 객체가 생성될 때 __init__ 메서드에서 실행된다.

만약, 메타클래스를 검증용으로 사용하면 오류를 더 빨리 일으킬 수 있다.

(즉, 서브클래스가 정의되는 시점부터 제대로 구성되었음을 보장하려면 메타클래스를 사용하면 된다)

 

메타클래스는 type을 상속하여 정의한다.

메타클래스는 기본으로 자체의 __new__ 메서드에서 연관된 class 문의 콘텐츠를 받는다.

여기서 타입이 실제로 생성되기 전에 클래스 정보를 수정할 수 있다.

class Meta(type):
    def __new__(meta, name, bases, class_dict):
        print((meta, name, bases, class_dict))
        return type.__new__(meta, name, bases, class_dict)

class MyClass(metaclass=Meta):
    stuff = 123
    
    def foo(self):
        pass

 

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

>>>
(<class '__main__.Meta'>, 
'MyClass', 
(), 
{'__module__': '__main__', 
'__qualname__': 'MyClass', 
'stuff': 123, 
'foo': <function MyClass.foo at 0x101def8b0>})

 

여러 면으로 이루어진 다각형을 어떤 타입이든 표현하고 싶다고 하자.

이렇게 하려면 특별한 검증용 메타클래스를 정의한 후 다각형 클래스 계층의 기반 클래스에 사용하면 된다.

 

이 때, 기반 클래스에는 같은 검증을 적용하지 말아야 한다는 점을 유의해야 한다.

class ValidatePolygon(type):
    def __new__(meta, name, bases, class_dict):
        if bases != ():  # 추상 Polygon 클래스는 검증하지 않음
            if class_dict['sides'] < 3:
                raise ValueError('Polygons need 3+ sides')
        return type.__new__(meta, name, bases, class_dict)
        
class Polygon(metaclass=ValidatePolygon):
    sides = None  # 서브클래스에서 설정함
    
    @classmethod
    def interior_angles(cls):
        return (cls.sides - 2) * 180

class Triangle(Polygon):
    sides = 3

 

만약 면이 세 개 미만인 다각형을 정의하려고 하면 검증 코드가 class 문의 본문이 끝나자마자 class 문을 실패하게 만든다.

print('Before class')
class Line(Polygon):
    print('Before sides')
    sides = 1
    print('After sides')
print('After class')


>>>
Before class
Before sides
After sides
Traceback ...
ValueError: Polygons need 3+ sides

 


 

메타클래스를 이용해서, 그것을 상속한 모든 클래스에 대해, 특정 검증을 진행할 수 있다는 것은

다르게 말하면, 메타클래스를 이용해서, 그것을 상속한 모든 클래스에서 공통된 메소드를 실행시킬 수 있다는 의미가 된다.

 

예를 들어, 모듈 방식의 파이썬 프로그램을 말들 때, 클래스 이름을 등록해두는 작업이 필요한데,

이때 메타클래스를 이용할 수 있다.

 

(자세한 내용은, 파이썬 코딩의 기술의 "Better Way 34. 메타클래스로 클래스의 존재를 등록하자." 챕터 참고)

class Meta(type):
    def __new__(meta, name, bases, class_dict):
        cls = type.__new__(meta, name, bases, class_dict)
        register_class(cls)
        return cls

 

반응형

댓글