메타클래스를 응용하는 가장 간단한 사례는 클래스를 올바르게 정의했는지 검증하는 것이다.
클래스 계층을 만들 때 스타일을 강제하거나, 메서드를 오버라이드하도록 요구하거나, 클래스 속성 사이에 관계를 두고 싶을 수도 있다.
보통 클래스 검증 코드는 클래스의 객체가 생성될 때 __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
'Programming Language > Python' 카테고리의 다른 글
[파이썬/Python] 문자열을 변수로 만들기, globals() locals() (0) | 2021.06.02 |
---|---|
Effective Python. 메타클래스로 클래스 속성에 주석을 달자. (0) | 2021.03.03 |
Effective Python. 지연 속성에는 __getattr__, __getattribute__, __setattr__을 사용하자. (0) | 2021.03.02 |
Effective Python. 재사용 가능한 @property 메서드에는 디스크립터를 사용하자. (0) | 2021.03.02 |
Effective Python. @property, @{property}.setter 사용 (0) | 2021.03.02 |