什么是元类
1.在Python中一切皆对象,类也是一个对象,实例对象由类创建出来的,类是由元类创建出来的。简而言之,用来创建类的类就叫元类(metaclass)。
2.函数type其实就是一个元类,type就是Python在背后用来创建所有类的元类。(type是内置的一个元类,所有的类都是由type实例化得到)
class Person: #生成一个简单的类
def __init__(self, name):
self.name = name
def score(self):
print('haha')
p = Person('kang')
a = Person
p1 = a('kang')
print(p1.name)
# 打印结果为 kang
#如何找元类
print(type(p))
<class '__main__.Person'> #这里查看的是p的数据类型
#type类是产生所有类的元类
print(type(Person))
print(type(dict))
print(type(list))
#查看类的数据类型,以下类型均为type
<class 'type'>
<class 'type'>
<class 'type'>
class底层原理
#class 类名 会把类构造出来
#实际上是: 元类实例化产生类 这个对象
#类实例化产生对象, 一定是: 类名()
#比如person类, 是由type实例化产生的, 传入一堆参数
# type() 就是调用类的__init__方法
#手动实现
#1. type()
#type(object_name, bases, dict)
#object_name:类的名字,是个字符串
#bases:是它的所有父类,基类
#dict: 名称空间,是一个字典
#通过type来直接产生类, 而不是用class关键字
l = {}
exec('''
school='oldboy'
def __init__(self,name):
self.name=name
def score(self):
print('分数是100')
''',{},l) #这里的{}表示全局名称空间, l表示局部名称空间
Person = type('Person, (object,), l') #按照上面的格式传值,就能用type来直接产生类
#这就实现了通过type直接来产生Person类
#class 底层就是调用type来实例化产生类
#exec() eval()的区别
#通过exec可以执行动态Python代码,
#而Python中的eval函数可以计算Python表达式,并返回结果;
#(exec不返回结果,print(eval("…"))打印None);
自定义元类控制类的产生
自定义元类;来控制类的产生:可以控制类名,可以控制类的继承父类,控制类的名称空间
#自定义元类必须继承type, 写一个类继承type 这种类就是元类
class Mymate(type):
#def __init__(self, *args,**kwargs):
def __init__(self, name, bases, dic):
#self 就是Person类
#print(name)
#print(bases)
#print(dic)
#练习一: 加限制, 控制类名必须以sb开头
#if not name.startswith('sb') #判断开头是否是'sb'
#raise Exception('类名没有以sb开头') 抛错
#练习二: 类必须加注释
print(self.__dcit__['__doc__'])
#metaclass = Mymeta 指定这个类生成的时候, 用自己写的Mymeta这个元类
class Person(object, metaclass = Mymeta):
'''
注释
'''
school = 'oldboy'
def __init__(self, name):
self,name = name
def score(self):
print('40分')
# p = Person()
#练习二: 控制类必须加注释
# class Mymeta(type):
# def __init__(self,name,bases,dic):
# print(self.__dict__['__doc__'])
# doc=self.__dict__['__doc__']
# if not doc:
# #没有加注释
# raise Exception('你的类没有加注释')
# class Person(object,metaclass=Mymeta):
# '''
# 我加了注释
# '''
# school='oldboy'
# def __init__(self,name):
# self.name=name
# def score(self):
# print('分数是100')
自定义元类控制类的执行过程
控制类的调用过程, 实际上是在控制:对象的产生
#__call__
class Mymeta(type):
def __call__(self, **args,**kwargs):
print(self) #self是People
print(args) #args = {'kang'}
print(kwargs) #kwargs = {'age':18}
#retuen 123
# 1.先造出一个People的空对象, 申请内存空间
# __new__ 方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。
obj = self.__new__(self) #虽然和上面的同样是People, 但是People没有, 找到的__new是父类的
#2. 为该空对象初始化独有的属性
self.__init__(obj, *args, **kwargs)
return obj
# People = Mymeta() People()则会触发__call__
calss People(object, metaclass=Mymeta):
country = 'China'
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print(f'{self.name} is eating')
#再调用Mymeta的__call__的时候, 首先找到自己(如下函数)的, 自己的没有才会找父类的
def __new__(cls, *args, **kwargs):
# print(cls) # cls是People
# cls.__new__(cls) #错误, 无限死循环, 自己找自己的, 会无限的递归下去
obj = super(People, cls).__new__(cls) # 使用父类的, 则是去父类中找__new__
return obj
- 类的调用,即类实例化就是元类的调用过程,可以通过元类Mymeta的__call__方法控制
- 分析:调用Pepole的目的
- 先造出一个People的空对象
- 为该对空对象初始化独有的属性
- 返回一个初始化好的对象
属性查找
查找顺序:
-
先对象层:OldoyTeacher->Foo->Bar->object
-
然后元类层:Mymeta->type
-
类的属性查找顺序:先从类本身中找--->mro继承关系去父类中找---->去自己定义的元类中找--->type中--->报错#对象的属性查找顺序:先从对象自身找--->类中找--->mro继承关系去父类中找--->报错
class Mymeta(type):
n=444
def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
obj=self.__new__(self)
# print(self.__new__ is object.__new__) #True
obj.__init__(*args, **kwargs)
return obj
class Bar(object):
# n=333
pass
# def __new__(cls, *args, **kwargs):
# print('Bar.__new__')
class Foo(Bar):
# n=222
pass
# def __new__(cls, *args, **kwargs):
# print('Foo.__new__')
class OldboyTeacher(Foo,metaclass=Mymeta):
# n=111
school='oldboy'
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s says welcome to the oldboy to learn Python' %self.name)
# def __new__(cls, *args, **kwargs):
# print('OldboyTeacher.__new__')
o=OldboyTeacher('egon',18) #触发OldboyTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找
print(OldboyTeacher.n)
# print(o.n)
作业
class Mymeta(type):
def __init__(self, name, bases, dic):
doc = self.__dict__.get('name')
if not doc:
raise Exception('你的名称空间中没有name字段')
print('存在name字段')
class Person(object,metaclass=Mymeta):
school='oldboy'
def __init__(self,name):
self.name=name
def score(self):
print('分数是100')
def name(self):
print('you')
class Mymeta(type):
def __call__(self, *args, **kwargs):
self.attr = kwargs
obj = self.__new__(self,*args,**kwargs)
return obj
class Mydic(dict, metaclass=Mymeta):
def __init__(self,**kwargs):
super().__init__(self, **kwargs)
def __getitem__(self, item):
return self[item]
def __setattr__(self, key, value):
self[key] = value
dic = Mydic(name='nick', age=18)
print(dic.__dict__)
print(dic.attr)