python基础----元类metaclass
1 引子
class Foo: pass f1=Foo() #f1是通过Foo类实例化的对象
python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例)
上例可以看出f1是由Foo这个类产生的对象,而Foo本身也是对象,那它又是由哪个类产生的呢?
#type函数可以查看类型,也可以用来查看对象的类,二者是一样的 print(type(f1)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 print(type(Foo)) # 输出:<type 'type'>
2 什么是元类?
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象
3 创建类的两种方式
方式一:
class Foo: def func(self): print('from func')
方式二:
def func(self): print('from func') x=1 Foo=type('Foo',(object,),{'func':func,'x':1})
4 一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的创建,工作流程是什么)
class Mytype(type): def __init__(self,class_name,bases=None,dict=None): print("Mytype init--->") print(class_name,type(class_name)) print(bases) print(dict) def __call__(self, *args, **kwargs): print('Mytype call---->',self,args,kwargs) obj=self.__new__(self) self.__init__(obj,*args,**kwargs) return obj class Foo(object,metaclass=Mytype):#in python3 #__metaclass__ = MyType #in python2 x=1111111111 def __init__(self,name): self.name=name def __new__(cls, *args, **kwargs): return super().__new__(cls) # return object.__new__(cls) #同上 f1=Foo('name') print(f1.__dict__) 自定制元类
class Mytype(type): def __init__(self,what,bases=None,dict=None): print('mytype init') def __call__(self, *args, **kwargs): obj=self.__new__(self) self.__init__(obj,*args,**kwargs) return obj class Foo(object,metaclass=Mytype): x=1111111111 def __init__(self,name): self.name=name def __new__(cls, *args, **kwargs): return super().__new__(cls) f1=Foo('egon') print(f1.__dict__) 自定制元类纯净版
class Mytype(type): def __init__(self,what,bases=None,dict=None): print(what,bases,dict) def __call__(self, *args, **kwargs): print('--->') obj=object.__new__(self) self.__init__(obj,*args,**kwargs) return obj class Room(metaclass=Mytype): def __init__(self,name): self.name=name r1=Room('alex') print(r1.__dict__) 自定制元类精简版
#元类总结 class Mymeta(type): def __init__(self,name,bases,dic): print('===>Mymeta.__init__') def __new__(cls, *args, **kwargs): print('===>Mymeta.__new__') return type.__new__(cls,*args,**kwargs) def __call__(self, *args, **kwargs): print('aaa') obj=self.__new__(self) self.__init__(self,*args,**kwargs) return obj class Foo(object,metaclass=Mymeta): def __init__(self,name): self.name=name def __new__(cls, *args, **kwargs): return object.__new__(cls) ''' 需要记住一点:名字加括号的本质(即,任何name()的形式),都是先找到name的爹,然后执行:爹.__call__ 而爹.__call__一般做两件事: 1.调用name.__new__方法并返回一个对象 2.进而调用name.__init__方法对儿子name进行初始化 ''' ''' class 定义Foo,并指定元类为Mymeta,这就相当于要用Mymeta创建一个新的对象Foo,于是相当于执行 Foo=Mymeta('foo',(...),{...}) 因此我们可以看到,只定义class就会有如下执行效果 ===>Mymeta.__new__ ===>Mymeta.__init__ 实际上class Foo(metaclass=Mymeta)是触发了Foo=Mymeta('Foo',(...),{...})操作, 遇到了名字加括号的形式,即Mymeta(...),于是就去找Mymeta的爹type,然后执行type.__call__(...)方法 于是触发Mymeta.__new__方法得到一个具体的对象,然后触发Mymeta.__init__方法对对象进行初始化 ''' ''' obj=Foo('egon') 的原理同上 ''' ''' 总结:元类的难点在于执行顺序很绕,其实我们只需要记住两点就可以了 1.谁后面跟括号,就从谁的爹中找__call__方法执行 type->Mymeta->Foo->obj Mymeta()触发type.__call__ Foo()触发Mymeta.__call__ obj()触发Foo.__call__ 2.__call__内按先后顺序依次调用儿子的__new__和__init__方法 '''
笔记:
class People: def __init__(self,name): self.name=name p=People('egon') # print(type(p)) # # print(type(People)) #typer--->类------>对象 class Foo: x=1 def run(self): pass print(type(Foo)) #type成为元类,是所有类的类,利用type模拟class关键字的创建类的过程 def run(self): print('%s is runing' %self.name) class_name='Bar' bases=(object,) class_dic={ 'x':1, 'run':run } Bar=type(class_name,bases,class_dic) print(Bar) print(type(Bar))
class Foo(metaclass=type): x=1 def run(self): print('running') type('Foo',(object,),{'x':1,'run':run}) class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): # print(self) # print(class_name) # print(class_bases) # print(class_dic) for key in class_dic: if not callable(class_dic[key]):continue if not class_dic[key].__doc__: raise TypeError('小子,你没写注释,赶紧去写') # type.__init__(self,class_name,class_bases,class_dic) class Foo(metaclass=Mymeta): x=1 def run(self): 'run function' print('running') Foo=Mymeta('Foo',(object,),{'x':1,'run':run}) print(Foo.__dict__) class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): pass def __call__(self, *args, **kwargs): # print(self) obj=self.__new__(self) self.__init__(obj,*args,**kwargs) #obj.name='egon' return obj class Foo(metaclass=Mymeta): x=1 def __init__(self,name): self.name=name #obj.name='egon' def run(self): 'run function' print('running') # print(Foo.__dict__) f=Foo('egon') print(f) print(f.name)