096 元类
Posted xichenhome
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了096 元类相关的知识,希望对你有一定的参考价值。
一、什么是元类
- 在python里面,我们都知道一切都是对象
- 我们用calss关键字定义一个类出来,那这个类肯定也就是一个对象。那这个类肯定又是由另一个类实例化的来的,负责产生这个对象的类就称之为是元类
- 产生类的类叫做元类
- 元类:简称为类的类
二、如何去找元类
- type是一个内置的元类,所有的类都是由type实例化得到的,产生类的类,叫元类
- type的实例就是type,并且它还继承了object类,而object类也是type类的实例。
class Person:
def __init__(self,name):
self.name=name
def score(self):
print('分数是100')
p=Person('nick')
a=Person
p=a('xichen')
print(p.name)
print(type(p))
<class ‘main.Person‘>
print(type(Person))
print(type(dict))
print(type(list))
print(type(str))
print(type(object))
print(type(type))
<class ‘type‘>
<class ‘type‘>
<class ‘type‘>
<class ‘type‘>
<class ‘type‘>
<class ‘type‘>
在这里我们发现,我们打印出来的结果都显示type是产生dict、list、str、的元类
**由此可以知道type是所有类的元类,并且type是继承object的类
2.1元类的继承关系图
三、class底层原理分析
class 类名,会把类构造出来
实际上是:class 底层就是调用type来实例化产生类(对象),需要传一堆参数。只不过class关键字帮我们干了这个事情
type(object_or_name, bases, dict)
object_or_name:类的名字,是个字符串
bases:是它的所有父类,基类
dict:名称空间,是一个字典
3.1通过type来直接产生类,不用class关键字了
def __init__(self,name)
self.name = name
Person = type('Person',(object,),'age':'18','__init__':__init__)
print(Person.__dict__)
‘school‘: ‘oldboy‘, ‘init‘: <function init at 0x00000273833A1E18>, ‘module‘: ‘main‘, ‘dict‘: <attribute ‘dict‘ of ‘Person‘ objects>, ‘weakref‘: <attribute ‘weakref‘ of ‘Person‘ objects>, ‘doc‘: None
(<class ‘object‘>,)
在这里我们可以看到我们打印Person这个类的父类的时候,我们发现它是继承的是object类,虽然它是用type元类实例化出来的类,这就进一步说明了type在实例化类对象的时候,其实是继承object的原理
3.2通过exec内置函数和type创建类,不用class关键字了
l=
exec('''
school='oldboy'
def __init__(self,name):
self.name=name
def score(self):
print('分数是100')
''',,l)
def __init__(self,name):
self.name=name
Person=type('Person',(object,),l)
print(Person.__dict__)
‘school‘: ‘oldboy‘, ‘init‘: <function init at 0x000001B15A301E18>, ‘score‘: <function score at 0x000001B15C175620>, ‘module‘: ‘main‘, ‘dict‘: <attribute ‘dict‘ of ‘Person‘ objects>, ‘weakref‘: <attribute ‘weakref‘ of ‘Person‘ objects>, ‘doc‘: None
print(Person.__bases__)
(<class ‘object‘>,)
p=Person('xichen')
print(p.name)
xichen
print(p.__dict__)
‘name‘: ‘xichen‘
四、通过元类来控制类的产生
自定义元类控制类的产生:可以控制类名,可以控制类的继承父类,控制类的名称空间
自定义元类必须继承type,如果一个类继承type, 这种类都叫元类
先写元类,在通过元类去控制类的产生,要在类继承的括号中写上metaclass=元类名
表示使用的元类
class Mymeta(type):
# def __init__(self,*args,**kwargs): # 正常写法,但是我们已经知道type传递的三个参数的意义
def __init__(self,name,bases,dic):
# self 就是Person类
print(name) # 创建的类名
print(bases) # 继承的所有父类
print(dic) # 类中属性,是一个字典
#加限制 控制类名:必须以sb开头
if not name.startswith('sb'):
raise Exception('类名没有以sb开头')
#类必须加注释
print(self.__dict__['__doc__'])
doc=self.__dict__['__doc__']
if not doc:
#没有加注释
raise Exception('你的类没有加注释')
#metaclass=Mymeta 指定这个类生成的时候,用自己写的Mymeta这个元类
class Person(object,metaclass=Mymeta):
age='18'
def __init__(self,name):
self.name=name
def score(self):
print('分数是100')
p=Person("xichen")
Person
(<class ‘object‘>,)
‘module‘: ‘main‘, ‘qualname‘: ‘Person‘, ‘doc‘: ‘\\n 注释\\n ‘, ‘age‘: ‘18‘, ‘init‘: <function Person.__init__ at 0x000001B97FDF19D8>, ‘score‘: <function Person.score at 0x000001B97FDF1A60>
Exception: 类名没有以sb开头
五、通过自定义元类控制类的调用过程
控制类的调用过程,实际上在控制:对象的产生 __call__
之前的内容说过,__call__
当对象加括号就会调用它。那么现在放到类对象也同样适用。
但只是说过类名()
实例化会先去调用init绑定方法,现在又说call是不是很混乱。
实际上,类名()实例化是先去调用call方法,再去调用init方法
如:
class Mymeta(type):
def __call__(self, *args, **kwargs):
print('xxx')
return 1
class Person(object,metaclass=Mymeta):
age='18'
def __init__(self,name):
self.name=name
def score(self):
print('分数是100')
#先触发元类的__call__,再触发init的执行
p=Person('xichen') # 当类对象加括号实际上就是去调用了自定义元类中的__call__方法
print(p) # 所以p等于1
# print(p.name) # 没有返回对象,p等于1所以一定会报错
那么,如果像上面那样,不就得不到实例化对象了嘛?
实际上,实例化创建对象是通过call方法中的new方法。这些call方法,new方法,init都是一些魔法方法
但是new方法只会得到一个新的对象,什么都没有的对象。
但我们实例化得到对象的本事是为了共享类中属性和方法,需要绑定上属性和方法。这时候就需要再使用init方法
如:
class Mymeta(type):
def __call__(self, *args, **kwargs):
# self 就是Person类
print(2)
print(self.mro()) # 类对象的属性查找顺序
# obj=self.__new__(self) # 方法1,通过self的new方法创建对象
obj=object.__new__(self) # 方法2,通过object的new方法创建对象
obj.__init__(*args, **kwargs) # 调用对象的init方法
return obj
def __init__(self,*args):
print(3)
print(args)
class Person(metaclass=Mymeta): # 实际上是先调用元类的call方法,再回调自定义元类的init方法
age = '18'
def __init__(self, name): # 当类实例化生成对象后,会通过call调用init方法
print(1)
self.name = name
p = Person(name='xichen') # 先调用自定义元类的call方法
print(p.__dict__)
print(p.name)
print(p)
3
(‘Person‘, (), ‘module‘: ‘main‘, ‘qualname‘: ‘Person‘, ‘age‘: ‘18‘, ‘init‘: <function Person.__init__ at 0x0000012BFC236A60>)
2
[<class ‘main.Person‘>, <class ‘object‘>]
1
‘name‘: ‘xichen‘
xichen
<main.Person object at 0x0000012BF53229B0>
六、有了元类之后的属性查找顺序
- 类的属性查找顺序:先从类本身中找--->mro继承关系去父类中找---->去自己定义的元类中找--->type中--->报错
- 对象的属性查找顺序:先从对象自身找--->类中找--->mro继承关系去父类中找--->报错
以上是关于096 元类的主要内容,如果未能解决你的问题,请参考以下文章