9.3 元类
一、元类的定义
python中一切皆对象,类也是一个对象,那么,创建类的是什么呢?
class Person: passp=Person()
Person类也是一个对象,那它一定也是由一个类实例化得到的,那么这个类就叫元类。
type是内置的一个元类,所有的类都是由type实例化得到的。
总结:元类就是产生类的类!
二、class底层原理分析
class 类名 : class关键字会把类构造出来。
实际上,内部是由元类(type)实例化产生这个类的。
怎么产生的呢,就是用type()传入一堆参数,实现的。
语法
type()type(object_or_name, bases, dict) object_or_name:类的名字,是个字符串 bases:是它的所有父类,基类 dict:名称空间,是一个字典
通过type()创建一个类,不适用class关键字。
l={}exec('''school='oldboy'def __init__(self,name): self.name=namedef score(self): print('分数是100')''',{},l)People = type('People',(object,),l)p=Person('nick')print(p.name) # nickprint(p.__dict__) # {'name': 'nick'}
exec()
exec 执行储存在字符串或文件中的 Python 语句,相比于 eval,exec可以执行更复杂的 Python 代码。
exec(object[, globals[, locals]])
- object:必选参数,表示需要被指定的Python代码。它必须是字符串或code对象。如果object是一个字符串,该字符串会先被解析为一组Python语句,然后在执行(除非发生语法错误)。如果object是一个code对象,那么它只是被简单的执行。
- globals:可选参数,表示全局命名空间(存放全局变量),如果被提供,则必须是一个字典对象。
- locals:可选参数,表示当前局部命名空间(存放局部变量),如果被提供,可以是任何映射对象。如果该参数被忽略,那么它将会取与globals相同的值。
返回值:exec 返回值永远为 None。
通过元类来控制类的产生
自定义元类来控制类的产生,可以控制类名,类的继承关系以及类的名称空间!
type:自定义元类必须继承type,继承了type的类都叫元类。
class Mymeta(type): def __init__(self,name,bases,dic): # 此时的self为Person这个类(Person类也是对象) print(name) print(bases) print(dic) # metaclass=Mymeta 指定这个类生成的时候,用自己写的Mymeta这个元类class Person(object,metaclass = Mymeta): school='oldboy' def __init__(self,name): self.name=name def score(self): print('分数是100')p = Person('nick')'''Person(,){'__module__': '__main__', '__qualname__': 'Person', '__doc__': '\n 注释\n ', 'school': 'oldboy', '__init__': , 'score': }'''
练习:控制类名必须全大写,控制类必须加注释!
class Mymeta(type): def __init__(self,name,bases,dic): # 此时的self为Person这个类(Person类也是对象) if not name.isupper(): raise Exception('类名必须全大写 。') doc = self.__dict__['__doc__'] if not doc: raise Exception('你的类没有加注释') # metaclass=Mymeta 指定这个类生成的时候,用自己写的Mymeta这个元类class Person(object,metaclass = Mymeta): school='oldboy' def __init__(self,name): self.name=name def score(self): print('分数是100')p = Person('nick')
通过元类控制类的调用过程
控制类的调用过程,实际上是在控制对象的产生。
class Mymeta(type): # Person('nick') 自动触发init,先触发元类的__call__ def __call__(self, *args, **kwargs): return self.__dict__ # 返回的是People类的对象p,无法点出属性/方法。class Person(object,metaclass=Mymeta): school='oldboy' def __init__(self,name): self.name=name def score(self): print('分数是100')p=Person('nick')print(p)
练习:把对象中的所有属性设置成私有的
class Mymeta(type): def __call__(self, *args, **kwargs): # 实例化产生一个Person类的对象,借助__new__来产生,需要把类传过去,才能产生对象 obj=object.__new__(self) # obj 是Person类的对象,只不过是空的 # 对象调用__init__方法完成初始化 obj.__init__(*args, **kwargs) # 修改对象属性为私有属性(拼接_*__*) obj.__dict__={ '_%s__%s'%(self.__name__,k):v for k,v in obj.__dict__.items()} return objclass Person(object, metaclass=Mymeta): school = 'oldboy' def __init__(self, name): self.name = name def score(self): print('分数是100') p = Person(name='nick')print(p.__dict__) # {'_Person__name': 'nick'}print(p.name) # AttributeError: 'Person' object has no attribute 'name'
有了元类之后的属性查找顺序
类的属性查找顺序:类自身-->按mro继承关系顺序去父类找-->自定义元类中找-->type-->报错
对象的查找顺序:先从对象自身找--->类中找--->mro继承关系去父类中找--->报错