커스텀 메타클래스
이제 우리는 이미 메타클래스가 무엇인지 알고 있습니다. 그래서 처음부터 끝까지 우리는 여전히 메타클래스의 용도가 무엇인지 모릅니다. 방금 메타클래스에 대해 배웠습니다. 사용법을 이해하기 전에 먼저 메타클래스를 사용자 정의하는 방법을 이해해 보겠습니다. 사용자 정의 방법을 이해해야만 기능을 더 잘 이해할 수 있기 때문입니다.
먼저 문자 그대로 메타클래스로 번역되는 __metaclass__ 속성
metaclass을 이해해 보겠습니다. 간단한 설명은 다음과 같습니다.
클래스를 정의한 후 이 클래스를 기반으로 인스턴스를 생성할 수 있습니다. 따라서 클래스를 먼저 정의하고, 그런 다음 인스턴스를 생성합니다.
하지만 클래스를 만들고 싶다면 어떻게 해야 할까요? 그런 다음 메타클래스를 기반으로 클래스를 생성해야 하므로 먼저 메타클래스를 정의한 다음 클래스를 생성합니다.
연결은 다음과 같습니다. 먼저 메타클래스를 정의한 다음 클래스를 생성하고 마지막으로 인스턴스를 생성할 수 있습니다.
그래서 메타클래스를 사용하면 클래스를 생성하거나 수정할 수 있습니다. 즉, 클래스는 메타클래스에 의해 생성된 "인스턴스"라고 생각하면 됩니다.
class MyObject(object): __metaclass__ = something… […]
이렇게 작성하면 Python은 메타클래스를 사용하여 MyObject 클래스를 생성합니다. MyObject(객체) 클래스를 작성할 때 MyObject 클래스 객체가 아직 메모리에 생성되지 않았습니다. Python은 클래스 정의에서 __metaclass__ 속성을 찾습니다. 발견되면 Python은 이를 사용하여 MyObject 클래스를 생성합니다. 발견되지 않으면 내장 유형 함수를 사용하여 클래스를 생성합니다. 그래도 이해가 되지 않는다면 아래 흐름도를 살펴보세요.
또 다른 예:
class Foo(Bar): pass
판단 과정은 무엇인가요?
먼저 Foo에 __metaclass__ 속성이 있는지 확인하세요. 있는 경우 Python은 __metaclass__를 통해 메모리에 Foo라는 클래스 객체를 생성합니다(이것은 클래스 객체라는 점에 유의하세요). Python이 __metaclass__를 찾지 못하면 Bar(상위 클래스)에서 __metaclass__ 속성을 계속 찾고 이전과 동일한 작업을 수행하려고 시도합니다. Python이 상위 클래스에서 __metaclass__를 찾을 수 없으면 모듈 계층 구조에서 __metaclass__를 찾아 동일한 작업을 시도합니다. __metaclass__가 여전히 발견되지 않으면 Python은 내장 유형을 사용하여 클래스 객체를 생성합니다.
사실 __metaclass__는 클래스의 동작을 정의합니다. 클래스가 인스턴스의 동작을 정의하는 방식과 유사하게 메타클래스는 클래스의 동작을 정의합니다. 클래스는 메타클래스의 인스턴스라고 할 수 있습니다.
이제 우리는 기본적으로 __metaclass__ 속성을 이해했지만 이 속성을 어떻게 사용하는지, 이 속성에 무엇을 넣을 수 있는지에 대해서는 아직 이야기하지 않았습니다.
답은: 수업을 만들 수 있다는 것입니다. 그러면 클래스를 만드는 데 무엇을 사용할 수 있나요? 유형 또는 유형이나 하위 클래스 유형을 사용하는 모든 것.
메타클래스의 주요 목적은 클래스가 생성될 때 자동으로 클래스를 변경하는 것입니다. 일반적으로 현재 컨텍스트에 맞는 클래스를 생성하려는 API에 대해 이와 같은 작업을 수행합니다. 모듈의 모든 클래스 속성을 대문자로 사용해야 한다고 결정하는 어리석은 예를 상상해 보세요. 이를 수행하는 방법에는 여러 가지가 있지만 그 중 하나는 모듈 수준에서 __metaclass__를 설정하는 것입니다. 이 방법을 사용하면 이 모듈의 모든 클래스가 이 메타클래스를 통해 생성됩니다. 모든 속성을 대문자로 변경하도록 메타클래스에 지시하기만 하면 됩니다.
다행히 __metaclass__는 실제로 임의로 호출할 수 있으므로 정식 클래스일 필요는 없습니다. 그럼 간단한 함수를 예로 들어 시작해 보겠습니다.
# 元类会自动将你通常传给‘type’的参数作为自己的参数传入 def upper_attr(future_class_name, future_class_parents, future_class_attr): '''返回一个类对象,将属性都转为大写形式''' # 选择所有不以'__'开头的属性 attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__')) # 将它们转为大写形式 uppercase_attr = dict((name.upper(), value) for name, value in attrs) # 通过'type'来做类对象的创建 return type(future_class_name, future_class_parents, uppercase_attr) __metaclass__ = upper_attr # 这会作用到这个模块中的所有类 class Foo(object): # 我们也可以只在这里定义__metaclass__,这样就只会作用于这个类中 bar = 'bip' print hasattr(Foo, 'bar') # 输出: False print hasattr(Foo, 'BAR') # 输出:True f = Foo() print f.BAR # 输出:'bip' 用 class 当做元类的做法: # 请记住,'type'实际上是一个类,就像'str'和'int'一样 # 所以,你可以从type继承 class UpperAttrMetaClass(type): # __new__ 是在__init__之前被调用的特殊方法 # __new__是用来创建对象并返回之的方法 # 而__init__只是用来将传入的参数初始化给对象 # 你很少用到__new__,除非你希望能够控制对象的创建 # 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__ # 如果你希望的话,你也可以在__init__中做些事情 # 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用 def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr): attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__')) uppercase_attr = dict((name.upper(), value) for name, value in attrs) return type(future_class_name, future_class_parents, uppercase_attr)
그러나 이 접근 방식은 실제로 OOP가 아닙니다. type을 직접 호출했으며 상위 클래스의 __new__ 메서드를 재정의하지 않았습니다. 이제 다음과 같이 해보겠습니다:
class UpperAttrMetaclass(type): def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr): attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__')) uppercase_attr = dict((name.upper(), value) for name, value in attrs) # 复用type.__new__方法 # 这就是基本的OOP编程,没什么魔法 return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)
추가 매개변수 upperattr_metaclass가 있다는 것을 눈치채셨을 것입니다. 여기에는 특별한 것이 없습니다. 클래스 메서드의 첫 번째 인수는 일반 클래스 메서드의 self 인수와 마찬가지로 항상 현재 인스턴스를 나타냅니다. 물론 여기서는 명확성을 위해 이름을 더 길게 만들었습니다. 그러나 self와 마찬가지로 모든 매개변수에는 전통적인 이름이 있습니다. 따라서 실제 프로덕션 코드에서 메타클래스는 다음과 같습니다.
class UpperAttrMetaclass(type): def __new__(cls, name, bases, dct): attrs = ((name, value) for name, value in dct.items() if not name.startswith('__') uppercase_attr = dict((name.upper(), value) for name, value in attrs) return type.__new__(cls, name, bases, uppercase_attr)
상속을 용이하게 하는 super 메소드를 사용하면 좀 더 명확하게 만들 수 있습니다(예, 메타클래스를 가질 수 있고, 메타클래스에서 상속하고, 유형에서 상속할 수 있습니다)
class UpperAttrMetaclass(type): def __new__(cls, name, bases, dct): attrs = ((name, value) for name, value in dct.items() if not name.startswith('__')) uppercase_attr = dict((name.upper(), value) for name, value in attrs) return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)
보통 우리는 내부 검사, 상속 제어 등에 의존하여 모호한 작업을 수행하기 위해 메타클래스를 사용합니다. 실제로 메타클래스를 사용하여 "암흑 마법"을 수행하고 따라서 복잡한 작업을 생성하는 것이 특히 유용합니다. 그러나 메타클래스 자체에 관한 한 실제로는 매우 간단합니다.
클래스 생성을 가로채기
클래스 수정
수정된 클래스 반환