元类
类也是一个对象,类究竟是从什么地方产生的呢. 类其实就是从元类产生的,每个类,就是元类的一个实例.(注意,和继承不同) 元类是用来控制如何创建类的,是类的模板,元类的实例就是类. 定义一个类,然后显示类名.__class__可以看到,类的类是type type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象 type的使用方法:F2= type('Fooo',(object,),{'x':1,'y':'test'})
f1 = F2()
type 接收三个参数:第 1 个参数是字符串 ‘Foo’,表示类名;第 2 个参数是元组 (object, ),把要继承的类放到这个元组里;第 3 个参数是字典,这里是一个空字典,表示没有定义属性和方法.如果有属性和方法,都可以放入该字典.
定制自己的元类并且通过metaclass指定元类
可以通过继承type类来定制一个自己的元类,然后通过自己的元类生成类.class Mytype(type):
def __init__(self):
pass
class Foo(metaclass=Mytype):
def __init__(self,name):
self.name = name
结果发现报错 TypeError: init() takes 1 positional argument but 4 were given.
这是因为在Foo类里指定元类的时候,由于元类还不存在,所以触发了Mytype的init方法,结果说我们写在Mytype里的init方法只接受了一个参数但是给过来4个,不过现在不知道给过来的是什么,那就先设置4个参数然后打印一下看看吧
class Mytype(type):
def __init__(self,a,b,c):
print(self)
print(a)
print(b)
print(c)
class Foo(metaclass=Mytype): # 就像执行了Foo = Mytype('Foo',(),{...})
def __init__(self,name):
self.name = name
结果分别是:
<class '__main__.Foo'>
Foo
()
{'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x000001C6BE8F60D0>}
在指定元类的时候发生了什么,可见self是Foo类,传了进来,因为类相当于元类的实例.之后是Foo的名称,一个元组,一个字典.猜想这里肯定是和type的参数一样.也就是指定元类的类,在生成自己的时候,会把自己的参数交给元类来实例化出自己这个类.
继续分析,来生成一个Foo的实例:
f1 = Foo('test') # 这是在调用Foo,也就是call Foo
# call的方法是在上层定义的,所以Foo的call方法是来自于Foo的元类,也就是Mytype.__call__.
但是先在Mytype里没有定义__call__,所以这个方法其实来自于type类.如果我们自己定义一个__call__方法来看看.
class Mytype(type):
def __init__(self,a,b,c):
print(self)
print(a)
print(b)
print(c)
def __call__(self, *args, **kwargs):
return
class Foo(metaclass=Mytype):
def __init__(self,name):
self.name = name
f1 = Foo('test') # 这是在调用Foo,也就是call Foo
print(f1.name) # 报错,AttributeError: 'NoneType' object has no attribute 'name'
由于我们自定义的call方法返回了None,这个时候会发现,f1实例化的过程就是调用Mytype的call,结果返回None,则f1就是None.
实例化的过程就是把Foo类和带有的参数都传给了__call__方法.来试验一下:
# Foo()实例化就是调用Foo的元类的__call__方法
class Mytype(type):
def __init__(self, a, b, c):
pass
def __call__(self, *args, **kwargs):
print(self)
print(args, kwargs)
return
class Foo(metaclass=Mytype):
def __init__(self, name):
self.name = name
f1 = Foo('test') # 这是在调用Foo,也就是call Foo
结果是:
<class '__main__.Foo'> # self
,是把Foo类作为元类的实例穿进去
('test',) {} # 这些就是Foo()调用时括号内的参数
那么至此就明白了,元类的__call__方法需要返回一个子类的对象.
class Mytype(type):
# def __init__(self, a, b, c):
# print(a)
def __call__(cls, *args, **kwargs):
obj = object.__new__(cls) # 利用内置的所有类继承的类object.__new__方法来生成一个Foo类的空对象
cls.__init__(obj, *args, **kwargs) # 这一步很关键,Foo的初始化方法就是给Foo对象加上属性的方法,所以这里,要把obj给弄到Foo的init方法里过一下,这个对象才能带上属性
return obj
class Foo(metaclass=Mytype):
def __init__(self, name):
self.name = name
f1 = Foo('test') # 这是在调用Foo,也就是call Foo
print(f1.name)
这就是元类的奥秘,调用类生成实例,就是调用类的元类的__call__方法生成一个这个类的对象交给了变量名. 如果去掉类与对象的概念,用一切皆对象来划分的话,元类-类-对象分别是一级,二级,三级对象,想调用某一个级别的对象,就要在高一级的对象上弄一个call方法.
关于元类,有Stack Overflow上有一个很好的解释:What are metaclasses in Python?,对应的中文翻译.
元类的玩法已经是python里的高级玩法了,这个回答总结的很棒:
Indeed, metaclasses are especially useful to do black magic, and therefore complicated stuff. But by themselves, they are simple:
intercept a class creation # 截获类的创建过程
modify the class # 定制类
return the modified class # 返回修改过的类