Reference: https://zhuanlan.zhihu.com/p/98440398 https://realpython.com/python-metaclasses/

1. 用户自定义类是type的实例

在Python中,所有的用户自定义的类,都是type这个类的实例

  1. class MyClass:
  2. pass
  3. print(type(MyClass))
  4. # -----output-------
  5. <class 'type'>

可以看到, MyClass这个类实际上是type的实例。

2. type的call运算符

当我们定义一个类的时候,真正发生的情况是,Python调用type的__call__运算符。

  1. class MyClass:
  2. pass
  3. # --------------------
  4. # Python真实的执行是
  5. # class = type(name, bases, dic)

上面的 type(classname, superclasses, attributedict) 就是 type 的 call 运算符重载,其会进一步调用:

  1. type.__new__(typeclass, name, bases, dic)
  2. type.__init__(class, name, bases, dic)

进一步解释一下:

  • name 执行类的名称,即class的name
  • bases 即class的bases
  • dic 即namespace dictionary,即class的dict

举个例子验证一下:

  1. # class = type(classname, superclasses, attributedict)
  2. MyClass_2 = type("MyClass", (), {'data': 1})
  3. # 等效于
  4. # class MyClass_2:
  5. # data = 1
  6. instance = MyClass_2
  7. print(instance.data)
  8. # ------output------------
  9. 1

正常的 MyClass 定义,和你手工去调用 type 运算符的结果是完全一样的。
再举个例子

  1. def func(obj):
  2. print(obj.attr)
  3. Foo = type('Foo', (), {"attr":100, "get_attr": func})
  4. x = Foo()
  5. x.get_attr()

3. 自定义

我们可以利用上述特性,先自定义一个类,继承自type,然后重载call, new, init函数,实现一个简单的metaclass,基于这个metaclass创建自定义类。

  1. class Mymeta(type):
  2. def __init__(self, name, bases, dic):
  3. super().__init__(name, bases, dic)
  4. print("Mymeta.__init__")
  5. def __new__(cls, *args, **kwargs):
  6. print("Mymeta.__new__")
  7. return type.__new__(cls, *args, **kwargs)
  8. def __call__(cls, *args, **kwargs):
  9. print("Mymeta.__call__")
  10. obj = cls.__new__(cls)
  11. cls.__init__(cls, *args, **kwargs)
  12. return obj
  13. class Foo(metaclass=Mymeta):
  14. def __init__(self):
  15. print('Foo.__init__')
  16. def __new__(cls, *args, **kwargs):
  17. print('Foo.__new__')
  18. return object.__new__(cls)
  1. # 上述代码直接运行结果如下
  2. Mymeta.__new__
  3. Mymeta.__init__

可以看到,metaclass在导入的时候,就先执行了 newinit运算符。

  1. # 在代码后面添加一个实例化
  2. foo = Foo()
  3. # 结果如下
  4. Mymeta.__new__
  5. Mymeta.__init__
  6. Mymeta.__call__
  7. Foo.__new__
  8. Foo.__init

同样,如果作为package被其他程序import,则在import的时候,就会执行

  1. import myPackage
  2. # 执行结果如下
  3. Mymeta.__new__
  4. Mymeta.__init__

总结:从上面的运行结果可以发现在定义 class Foo() 定义时,会依次调用 MyMeta 的 newinit 方法构建 Foo 类,然后在调用 foo = Foo() 创建类的实例对象时,才会调用 MyMeta 的 call 方法来调用 Foo 类的 newinit 方法。

4. 是否有必要?

  1. # 简单的实现
  2. class Base:
  3. attr = 100
  4. class X(Base):
  5. pass
  6. class Y(Base):
  7. pass
  8. class Z(Base):
  9. pass
  1. # 类装饰器
  2. def decorator(cls):
  3. class NewClass(cls):
  4. attr = 100
  5. return NewClass
  6. @decorator
  7. class X:
  8. pass
  9. X.attr