Reference: https://zhuanlan.zhihu.com/p/98440398 https://realpython.com/python-metaclasses/
1. 用户自定义类是type的实例
在Python中,所有的用户自定义的类,都是type这个类的实例
class MyClass:passprint(type(MyClass))# -----output-------<class 'type'>
可以看到, MyClass这个类实际上是type的实例。
2. type的call运算符
当我们定义一个类的时候,真正发生的情况是,Python调用type的__call__运算符。
class MyClass:pass# --------------------# Python真实的执行是# class = type(name, bases, dic)
上面的 type(classname, superclasses, attributedict) 就是 type 的 call 运算符重载,其会进一步调用:
type.__new__(typeclass, name, bases, dic)type.__init__(class, name, bases, dic)
进一步解释一下:
- name 执行类的名称,即class的name
- bases 即class的bases
- dic 即namespace dictionary,即class的dict
举个例子验证一下:
# class = type(classname, superclasses, attributedict)MyClass_2 = type("MyClass", (), {'data': 1})# 等效于# class MyClass_2:# data = 1instance = MyClass_2print(instance.data)# ------output------------1
正常的 MyClass 定义,和你手工去调用 type 运算符的结果是完全一样的。
再举个例子
def func(obj):print(obj.attr)Foo = type('Foo', (), {"attr":100, "get_attr": func})x = Foo()x.get_attr()
3. 自定义
我们可以利用上述特性,先自定义一个类,继承自type,然后重载call, new, init函数,实现一个简单的metaclass,基于这个metaclass创建自定义类。
class Mymeta(type):def __init__(self, name, bases, dic):super().__init__(name, bases, dic)print("Mymeta.__init__")def __new__(cls, *args, **kwargs):print("Mymeta.__new__")return type.__new__(cls, *args, **kwargs)def __call__(cls, *args, **kwargs):print("Mymeta.__call__")obj = cls.__new__(cls)cls.__init__(cls, *args, **kwargs)return objclass Foo(metaclass=Mymeta):def __init__(self):print('Foo.__init__')def __new__(cls, *args, **kwargs):print('Foo.__new__')return object.__new__(cls)
# 上述代码直接运行结果如下Mymeta.__new__Mymeta.__init__
可以看到,metaclass在导入的时候,就先执行了 new和init运算符。
# 在代码后面添加一个实例化foo = Foo()# 结果如下Mymeta.__new__Mymeta.__init__Mymeta.__call__Foo.__new__Foo.__init
同样,如果作为package被其他程序import,则在import的时候,就会执行
import myPackage# 执行结果如下Mymeta.__new__Mymeta.__init__
总结:从上面的运行结果可以发现在定义 class Foo() 定义时,会依次调用 MyMeta 的 new 和 init 方法构建 Foo 类,然后在调用 foo = Foo() 创建类的实例对象时,才会调用 MyMeta 的 call 方法来调用 Foo 类的 new 和 init 方法。
4. 是否有必要?
# 简单的实现class Base:attr = 100class X(Base):passclass Y(Base):passclass Z(Base):pass
# 类装饰器def decorator(cls):class NewClass(cls):attr = 100return NewClass@decoratorclass X:passX.attr
