metaclass,有人说是元类,有人说是超越变形类。总之就是站在高处的一个很厉害的东西。要了解metaclass,需要首先了解type的__call__函数。参考:https://www.jianshu.com/p/33d6688246a6
我们知道我们自定义的python类都是有type实例的重载__call__()函数创建的。如果有个东西,能够拦截python类的定义,不在有type实例创建,而变成我们自己定义的类创建呢,没错,那个东西就是metaclass。
metaclass 是 type 的子类,通过替换 type 的__call__运算符重载机制,“超越变形”正常的类。
我们创建一个metaclass类
class MyMetaClass(type):
def __init__(cls, name, bases, kwds):
super(MyMetaClass, cls).__init__(name, bases, kwds)
print("meta class __init__")
type(MyMetaClass)
#输出
type
再定义一个简单的类,设置他的metaclass为MyMetaClass
class MyClass(metaclass = MyMetaClass):
def __init__(self,data):
self.data = data
print("myclass init")
print("========")
print(type(MyClass))
###### 输出 ######
meta class __init__
========
<class '__main__.MyMetaClass'>
从上面的例子中可以看到,在定义MyClass的时候,就已经执行了MyMetaClass的init方法了。而最终的MyClass的type也变成了MyMetaClass。
更加直观一点的,我们换成继承自MyMetaClass.
class MyClass(MyMetaClass):
def __init__(self,data):
self.data = data
print("myclass init")
print("========")
print(type(MyClass))
###### 输出 ######
========
<class 'type'>
如果只是简单的继承,定义的时候不会执行MyMetaClass的init方法,继承下来的类型也还是type.
上面的代码似乎与__call__没什么关系,下面我们看一个比较完整的例子,有个更清晰的认识:
#metaclass类
class MyMetaClass(type):
def __init__(cls, name, bases, kwds):
super(MyMetaClass, cls).__init__(name, bases, kwds)
print("meta class __init__")
print("init cls is:{},name is:{},bases is:{},kwds is:{}".format(cls,name,bases,kwds))
print(cls.tag)
def __new__(cls,*args,**kwargs):
print("meta class __new__,new_cls:{}".format(cls))
return type.__new__(cls,*args,**kwargs)
def __call__(cls, *args, **kwargs):
print('=meta class __call__,call_cls:{}====='.format(cls))
obj = cls.__new__(cls)
cls.__init__(cls, *args, **kwargs)
return obj
#定义一个类,设置它的metaclass为MyMetaClass
class MyClass(metaclass = MyMetaClass):
tag = 'tag'
def __init__(self,data):
self.data = data
print("MyClass __init__")
def __new__(cls, *args, **kwargs):
print('MyClass.__new__')
return object.__new__(cls)
print("========")
print(type(MyClass))
#### 输出 #####
meta class __new__,new_cls:<class '__main__.MyMetaClass'>
meta class __init__
init cls is:<class '__main__.MyClass'>,name is:MyClass,bases is:(),kwds is:{'__module__': '__main__', '__qualname__': 'MyClass', 'tag': 'tag', '__init__': <function MyClass.__init__ at 0x7fb4b8725ee0>, '__new__': <function MyClass.__new__ at 0x7fb4b8725af0>}
tag
========
<class '__main__.MyMetaClass'>
#创建实例
myclass = MyClass(1)
print(myclass.data)
myclass = MyClass(2)
print(myclass.data)
#### 输出 #####
=meta class __call__,call_cls:<class '__main__.MyClass'>=====
MyClass.__new__
MyClass __init__
1
=meta class __call__,call_cls:<class '__main__.MyClass'>=====
MyClass.__new__
MyClass __init__
2
总结:
1、在定义类的时候,设置metaclass,则会先执行metaclass的__new__方法,返回的是一个metaclass对象。
2、执行完__new__之后,则调用metaclass的__init__方法,这时传入的cls为我们所定义的类。如果想对这个类在定义好之后(实例化前)做一些什么设置(如注册到某个全局变量中,则可在此进行操作)
3、在每次实例化类的时候,就会调用MetaClass的__call__()函数,而不是type的__call__()函数。从而实现了对python类定义的拦截
那么拦截对python类的定义有什么用处呢?metaclass与类装饰器、与继承比较呢?
首先,metaclass与类装饰器都与__call__()函数相关,类装饰器一般用于装饰函数,是装饰的函数变成类的一个(只一个)实例,使类的实例能像函数一样去调用。而metaclass用于类的设置,改变了类的定义,而且一个类可以创建多个实例
metaclass与继承比较,继承并不会改变类的定义,继承的类在定义时并不会有任何执行。继承不会改变类的创建,继承不会改变类的定义。
继承是两个类之间有一些共同的属性或方法,抽出这些共同的属性和方法使之成为父类。而设置metaclass的类之间并不一定要存在共同属性和方法。它主要用于在类定义完成之后,实例化之前,对类的操作。
metaclass改变了类的定义,创建,需要慎用。一般建议用于框架而非应用层面。