Reference: https://zhuanlan.zhihu.com/p/98440398 https://realpython.com/python-metaclasses/
1. 用户自定义类是type的实例
在Python中,所有的用户自定义的类,都是type这个类的实例
class MyClass:
pass
print(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 = 1
instance = MyClass_2
print(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 obj
class 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 = 100
class X(Base):
pass
class Y(Base):
pass
class Z(Base):
pass
# 类装饰器
def decorator(cls):
class NewClass(cls):
attr = 100
return NewClass
@decorator
class X:
pass
X.attr