2.5.2.1Python-黑魔法:元类
总目录:https://blog.****.net/qq_41106844/article/details/105553392
Python - 子目录:https://blog.****.net/qq_41106844/article/details/105553333
引子
class Foo:
pass
f=Foo()#f是Foo类实例化的对象
print(type(f))
print(type(Foo))
<class '__main__.Foo'>
<class 'type'>
Python的世界里有一个铁律,一切皆为对象,f是一个对象,Foo也是一个对象。而f是Foo这个类产生的对象,那么Foo是谁产生的呢?
我们从输出结果来看,Foo是type类型,那么type就是Foo的类,是用来创造类的类,这种类就是元类。
什么是元类
我们总结一下刚才的结论:
元类就是类的模板,是类的类。
元类的主要目的是为了控制类的创建行为。
type是Python的一个内建元类,用来直接控制生成类,在python当中任何class定义的类其实都是type类实例化的结果。
只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类,自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程。
使用元类
我们来看一下type的源码,能看到需要传三个参数,分别是类名,父类,属性
我们既然说type是元类,那么我们就可以通过type不适用class关键字创建类:
class Foo1:
x=1
print(Foo1)
<class '__main__.Foo1'>
print(type('Foo2',(object,),{'x':1}))
<class '__main__.Foo2'>
我们来看一看调用
class Foo1:
x=1
Foo2=type('Foo2',(object,),{'x':1})
print(Foo1.x)
1
print(Foo2.x)
1
上面这个例子我们只是添加了一个属性,接下来我们演示一下加入一个方法:
def test(self,name):
self.name = name
Foo2=type('Foo2',(object,),{'x':1,'test':test})
print(Foo2.__dict__)
{'x': 1, 'test': <function test at 0x000001B9BE0C8048>,。。。。
自定义元类
我们知道类有继承的属性,那么我们自定义一个类继承元类,是不是就可以根据我们的需要来自定义元类呢。
class Mytype(type):
def __init__(self,a,b,c):
print(a)
print(b)
print(c)
class Foo(metaclass=Mytype):
def __init__(self,name):
self.name =name
Foo
()
{'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x0000018B7D8DC268>}
首先我们知道在type源码中,__init__方法有四个参数,抛去cls,还有传入三个参数,所以我们自定义元类中必须有四个参数。
如果没有的话,会报错:
TypeError: __init__() takes 1 positional argument but 4 were given
而我们打印a,b,c三个参数接受的信息时,可以发现他们分别接受了类标识符,父类,属性。
接下来我们开始将类实例化:
class Mytype(type):
def __init__(self,a,b,c):
pass
class Foo(metaclass=Mytype):
def __init__(self,name):
self.name =name
f1=Foo('hanxuan')
print(f1)
<__main__.Foo object at 0x0000022F4506C320>
这里我们有一个疑问,类在实例化时,元类会干些什么呢?我们这里就需要一个魔法函数--__call__,具体用法看上一节。
同时我们要介绍两个关键的魔法函数:__new__和__init__。
__init__是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值。
__new__是在实例创建之前被调用的,因为它的任务就是创建实例然后返回该实例,是个静态方法。
类在进行实例化时,会先指向__new__完成实例化,之后执行__init__完成初始传参。
从这两个图就能清楚的分清楚,在执行f1=Foo('hanxuan')时,会指向obj=object.__new__(self),之后将实例化的信息传给f1。
实例
class Mytype(type):
def __init__(self,a,b,c):
pass
def __call__(self, *args, **kwargs):
obj=object.__new__(self)
self.__init__(obj,*args,**kwargs)
return obj
class Foo(metaclass=Mytype):
def __init__(self,name):
self.name =name
f1=Foo('hanxuan')
print(f1)
<__main__.Foo object at 0x00000186FCDEC320>