元类
元类
一、元类的定义
python中一切皆对象,类也是一个对象,那么,创建类的是什么呢?
class Person:
pass
p=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=name
def score(self):
print('分数是100')
''',{},l)
People = type('People',(object,),l)
p=Person('nick')
print(p.name) # nick
print(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
(<class 'object'>,)
{'__module__': '__main__', '__qualname__': 'Person', '__doc__': '\n 注释\n ', 'school': 'oldboy', '__init__': <function Person.__init__ at 0x03C0B4B0>, 'score': <function Person.score at 0x03C0B4F8>}
'''
练习:控制类名必须全大写,控制类必须加注释!
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 obj
class 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继承关系去父类中找--->报错