说实话,我真心不太想总结这个东西,算了,炒一下egon的吧
1 引子
1 class Foo: 2 pass 3 4 f1=Foo() #f1是通过Foo类实例化的对象
python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例)
上例可以看出f1是由Foo这个类产生的对象,而Foo本身也是对象,那它又是由哪个类产生的呢?
1 #type函数可以查看类型,也可以用来查看对象的类,二者是一样的 2 print(type(f1)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 3 print(type(Foo)) # 输出:<type 'type'>
2 什么是元类?
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象
3 创建类的两种方式
方式一:
1 class Foo: 2 def func(self): 3 print('from func')
方式二:
1 def func(self): 2 print('from func') 3 x=1 4 Foo=type('Foo',(object,),{'func':func,'x':1})
4 一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的创建,工作流程是什么)
自定制元类
自定制元类纯净版
自定制元类精简版
1 #元类总结 2 class Mymeta(type): 3 def __init__(self,name,bases,dic): 4 print('===>Mymeta.__init__') 5 6 7 def __new__(cls, *args, **kwargs): 8 print('===>Mymeta.__new__') 9 return type.__new__(cls,*args,**kwargs) 10 11 def __call__(self, *args, **kwargs): 12 print('aaa') 13 obj=self.__new__(self) 14 self.__init__(self,*args,**kwargs) 15 return obj 16 17 class Foo(object,metaclass=Mymeta): 18 def __init__(self,name): 19 self.name=name 20 def __new__(cls, *args, **kwargs): 21 return object.__new__(cls) 22 23 ''' 24 需要记住一点:名字加括号的本质(即,任何name()的形式),都是先找到name的爹,然后执行:爹.__call__ 25 26 而爹.__call__一般做两件事: 27 1.调用name.__new__方法并返回一个对象 28 2.进而调用name.__init__方法对儿子name进行初始化 29 ''' 30 31 ''' 32 class 定义Foo,并指定元类为Mymeta,这就相当于要用Mymeta创建一个新的对象Foo,于是相当于执行 33 Foo=Mymeta('foo',(...),{...}) 34 因此我们可以看到,只定义class就会有如下执行效果 35 ===>Mymeta.__new__ 36 ===>Mymeta.__init__ 37 实际上class Foo(metaclass=Mymeta)是触发了Foo=Mymeta('Foo',(...),{...})操作, 38 遇到了名字加括号的形式,即Mymeta(...),于是就去找Mymeta的爹type,然后执行type.__call__(...)方法 39 于是触发Mymeta.__new__方法得到一个具体的对象,然后触发Mymeta.__init__方法对对象进行初始化 40 ''' 41 42 ''' 43 obj=Foo('egon') 44 的原理同上 45 ''' 46 47 ''' 48 总结:元类的难点在于执行顺序很绕,其实我们只需要记住两点就可以了 49 1.谁后面跟括号,就从谁的爹中找__call__方法执行 50 type->Mymeta->Foo->obj 51 Mymeta()触发type.__call__ 52 Foo()触发Mymeta.__call__ 53 obj()触发Foo.__call__ 54 2.__call__内按先后顺序依次调用儿子的__new__和__init__方法 55 '''