한 레벨로 돌아가서 클래스 객체 자체가 어떻게 생성되는지 살펴보겠습니다.
type() 메소드가 객체의 유형을 확인하거나 객체가 생성된 클래스를 결정할 수 있다는 것을 알고 있습니다.
print(type(12)) print(type('python'))
<class 'int'> <class 'str'>
class A: passprint(type(A))
<class 'type'>
Pass 이 코드에서 볼 수 있듯이 클래스 객체 A는 type()에 의해 생성됩니다. 즉, 유형을 사용하여 새 객체를 생성할 수도 있고, 생성된 것은 클래스 객체이므로 모든 클래스 객체의 클래스입니다. :
print(type.__doc__)
type(object_or_name, bases, dict) type(object) -> the object's type type(name, bases, dict) -> a new type
class 클래스를 정의하는 구문은 실제로 type(name, bases, dict)로 변환됩니다. 여기서 name 매개변수는 클래스의 이름이고 bases는 상속받는 튜플입니다. 부모 클래스이고 dict가 속성과 메서드입니다.
class A: pass# 实际上等于B = type('A', (), {}) print(A.__name__ == B.__name__)
True
이론적으로는 메타클래스의 의미이지만, 실용적인 관점에서 보면 분명히 사용하기 더 편리하고 합리적입니다. 클래스 구문이며 메타클래스의 실제 의미는 클래스 유형을 상속하여 새로운 메타클래스를 구성하고 특정 작업을 수행하여 특정 동작을 가진 클래스 객체를 생성하는 것입니다. 이러한 관점에서 볼 때, 클래스 유형을 상속한다는 점을 제외하면 그 본질은 일반 클래스 객체와 다르지 않습니다.
인스턴스를 생성할 때 __init__ 메서드를 호출하여 초기화됩니다. 실제로 그 전에는 __new__ 메서드를 호출하여 인스턴스를 생성한 다음 마치 __new__가 변수 선언을 담당하는 것처럼 __init__에 의해 초기화됩니다. , __init__는 선언된 변수 초기화를 담당합니다. 여기에는 __new__(cls,)의 반환 값이 cls 매개 변수의 인스턴스여야 한다는 규칙이 있습니다. 그렇지 않으면 __init__이 트리거되지 않습니다. 예를 들어 enum.Enum의 정의에서는 열거형 유형이 싱글톤 모드이기 때문입니다. 이므로 __new__ 정의에서 해당 인스턴스가 반환되지 않으면 초기화되지 않습니다.
class Enum: def __new__(cls, value): print(cls, value) return value def __init__(self): print("Will not be called!") e = Enum(1)
<class '__main__.Enum'> 1
일반적으로 __new__를 직접 정의할 때 다음을 호출하여 cls 인스턴스를 생성해야 합니다. 상위 클래스의 __new__ 메소드, 그리고 메타클래스를 정의할 때 위에서 언급한 유형의 사용법을 호출할 때(메타클래스가 유형에서 상속되기 때문에):
class MetaEnum(type): def __new__(metaclass, name, base, attrs): print("Metaclass: {}\nName: {}\nParents: {}\nAttributes: {}".format(metaclass, name, base, attrs)) return super().__new__(metaclass, name, base, attrs)
class Enum(metaclass=MetaEnum): # Python 2.7 中定义元类的方法是使用 __metaclass__ 变量 # [PEP 3115](https://www.python.org/dev/peps/pep-3115/) # 将 Python 3.0 以后语法改为 class Cls(metaclass=Meta) test = 0
Metaclass: <class '__main__.MetaEnum'> Name: Enum Parents: () Attributes: {'__qualname__': 'Enum', '__module__': '__main__', 'test': 0}
이때 우리는 더 이상 유형이 아닌 Enum 클래스의 메타클래스 MetaEnum:
type(Enum)
__main__.MetaEnum
__new__ 메서드 외에도 PEP 3115 또한 초기화된 네임스페이스(즉, 유형의 세 번째 매개변수)를 설정하기 위한 __prepare__ 속성을 정의하거나 enum.Enum을 예로 들어 열거 유형의 속성 이름이 재사용되지 않도록 제한해야 하며 그런 다음 동작을 제한할 수 있습니다.
# 定义新的字典类,在赋值新的 dict[k] = v 时 # 检查 k 是否重复 class _EnumDict(dict): def __init__(self): super().__init__() self.members = [] def __setitem__(self, k, v): if k in self.members: raise TypeError("Attempted to reuse key: '{}'".format(k)) else: self.members.append(k) super().__setitem__(k, v) class MetaEnum(type): @classmethod def __prepare__(metaclass, cls, bases): return _EnumDict() def __new__(metaclass, name, base, attrs): return super().__new__(metaclass, name, base, attrs) class Enum(metaclass=MetaEnum): pass class Color(Enum): try: red = 1 red = 2 except TypeError:# 这里没有使用 as err: 的原因是? print("TypeError catched")
TypeError catched
Python의 모든 객체는 특정 클래스의 인스턴스이거나 특정 메타클래스 유형의 인스턴스입니다.
요약
메타클래스는 Python에서 상대적으로 심오한 흑마술이며 일반적인 일상 응용 프로그램에서는 일반적으로 사용되지 않을 수 있지만, 그 뒤에 있는 원리를 이해하는 것은 Python에서 필수적입니다. Python 객체 지향 프로그래밍을 이해하면 클래스를 크게 변경해야 하는 경우 최소한 어디서부터 시작해야 하는지 알면 도움이 됩니다.