元类
我们需要牢记这句话:
一切皆对象:类实际上也是一个对象
先来举个例子:
class Person: #Person实际上也是一个对象,一定有某个类实例化得到,而这个类就是元类
def __init__(self,name):
self.name = name
def score(self):
print('您的分数为99')
p = Person('nick')
print(p.name)
一、什么是元类
元类:实例化产生类的类
type
是内置的一个元类,所有的类都是type
实例化产生
那么如何找元类呢?
class Person: #Person实际上也是一个对象,一定有某个类实例化得到,而这个类就是元类
def __init__(self,name):
self.name = name
def score(self):
print('您的分数为99')
p = Person('nick')
print(p.name)
print(type(p)) #<class '__main__.Person'>
print(type(Person)) #<class 'type'>他们都是由type创建出来的
print(type(list)) #<class 'type'>他们都是由type创建出来的
print(type(str)) #<class 'type'>他们都是由type创建出来的
print(type(object)) #<class 'type'>他们都是由type创建出来的
print(type(dict)) #<class 'type'>他们都是由type创建出来的
print(type(type)) #<class 'type'>他们都是由type创建出来的
二、class底层原理分析
我们需要深刻理解下面这几段话
-
class 类名
会把类构造出来 - 实际上是元类
type
实例化产生类这个对象 -
类实例化产生对象一定是:
类名()
我们举个简单的例子:
class Person: #Person类是由type实例化产生,传一堆参数
pass
p = Person() #类实例化产生对象
此时我们会有疑问???
type
是怎样传一堆参数去实例化产生一个Person
类的呢?
我们打开type()
的源码,会发现他是这样的type(object_or_name, bases, dict)
因此它需要传3个参数分别是:
object_or_name
:类的名字(是个字符串) class_name = 'Peraon'
bases
: 是他的所有父类、基类们 class_bases = (object,)
(object,)
是个元组,所以要加个逗号
dict
:类的名称空间(方法和属性们)
需求:通过type
直接产生一个person
类,不用class
关键字
class Person:
def __init__(self,name):
self.name = name
def score(self):
print('您的分数为99')
p = Person('yjy')
print(p.name)
-------------------------------------------------------------------------
#方式一
#这是我们要定义的Person类的属性和方法
school='young_girl'
def __init__(self,name):
self.name = name
def score(self):
print('您的分数为99')
#用定义Person类
Person = type('Person',(object,),{'school':'young_girl','__init__':__init__,'score': '您的分数为99'})
p = Person('yjy') #实例化Person类产生对象p
print(Person.__bases__) #(<class 'object'>,)
print(Person.__dict__) #类的名称空间
print(p.name) #yjy
#方式二
l = {}
#exec:函数执行储存在字符串或文件中的 Python 语句
#exec将''' '''中的变量放到了局部变量l中,待会直接将l放在type()中
exec('''
school='young_girl'
def __init__(self,name):
self.name = name
def score(self):
print('您的分数为99')
''',{},l)
Person = type('Person',(object,),l)
p = Person('yjy') #实例化Person类产生对象p
print(Person.__bases__) #(<class 'object'>,)
print(Person.__dict__) #类的名称空间
p.score() #您的分数为99
- exec的用法
exec 执行储存在字符串或文件中的 Python 语句,相比于 eval,exec可以执行更复杂的 Python 代码
exec(object[, globals[, locals]]) #这是exec的语法
- object:必选参数,包含一系列Python代码中的字符串,该字符串会先被解析为一组Python语句,然后再执行
- globals:可选参数,全局作用域(字典形式)如果不指定,默认
globals
- locals:可选参数,局部作用域(字典形式)如果不指定,默认
locals
三、通过自定义元类控制类的产生
控制类的产生 == 控制元类的调用过程
自定义元类;来控制类的产生。可以控制类名,可以控制类的继承父类,控制类的名称空间
- 自定义元类必须继承type,写一个类继承
type
这种类都叫元类
举个例子:
class Mymeta(type): #只有继承了type类才能称为元类,否则只是普通的函数
print('这里可以写一些属性和方法')
#metaclass=Mymeta指定这个类生成的时候,用自己写的Mymeta这个元类
class Person(metaclass=Mymeta):
pass
p = Person()
练习一:写一个自定义元类,让Person
类继承
class Mymeta(type):
#def __init__(self,*args,**kwargs): #和下面这个对应只不过他更正规
def __init__(self,name,bases,dic):
#self就是Person类
print(name) #Person
print(bases) #(<class 'object'>,)
print(dict) #Person的名称空间
class Person(object,metaclass=Mymeta):
school='oldboy'
def __init__(self,name):
self.name=name
def score(self):
print('分数是100')
p=Person('yjy')
print(p.name) #yjy 而且Mymeta类中的三个print都会打印,就说明他继承了Mymeta
练习二:写一个自定义元类,限制类名必须以Yjy开头
#测试一
class Mymeta(type):
def __init__(self,name,*args,**kwargs):
if not name.startswith('Yjy'):
raise Exception('类名没有以Yjy开头,请做更改')
class Person(object,metaclass=Mymeta):
def __init__(self,name):
self.name =name
p = Person('hhh')
print(p.name) #Exception: 类名没有以Yjy开头,请做更改
------------------------------------------------------------
#测试二
class Mymeta(type):
def __init__(self,name,*args,**kwargs):
if not name.startswith('Yjy'):
raise Exception('类名没有以Yjy开头,请做更改')
class YjyPerson(object,metaclass=Mymeta):
def __init__(self,name):
self.name =name
p = YjyPerson('hhh')
print(p.name) #hhh
练习三:限制定义的类必须加注释
#测试一
class Mymeta(type):
def __init__(self,name,*args,**kwargs):
if not self.__dict__['__doc__']:
raise Exception('定义的类中没有加注释,请做更改')
class YjyPerson(object,metaclass=Mymeta):
def __init__(self,name):
self.name =name
p = YjyPerson('hhh')
print(p.name)
print(p.__doc__) #Exception: 定义的类中没有加注释,请做更改
---------------------------------------------------------
#测试二
class Mymeta(type):
def __init__(self,name,*args,**kwargs):
if not self.__dict__['__doc__']:
raise Exception('定义的类中没有加注释,请做更改')
class YjyPerson(object,metaclass=Mymeta):
'''我是类里面的注释,我必须要用三引号'''
def __init__(self,name):
self.name =name
p = YjyPerson('hhh')
print(p.name) #hhh
print(p.__doc__) #我是类里面的注释,我必须要用三引号
四、通过自定义元类控制类的调用过程
控制类的调用过程 == 对象的产生
class Mymeta(type):
def __call__(self, *args, **kwargs):
print('我是Mymeta元类中的__call__方法')
class Person(object,metaclass=Mymeta):
def __init__(self,name):
self.name = name
def __call__(self, *args, **kwargs):
print('我是Person类中的__call__方法')
#自动触发init的执行,此时先去触发元类中的__call__方法
p = Person('yjy') #我是Mymeta元类中的__call__方法
p() #TypeError: 'NoneType' object is not callable
ni你需要仔细看这两段代码有什么不同
class Mymeta(type):
def __call__(self, *args, **kwargs):
print('我是Mymeta元类中的__call__方法')
obj = object.__new__(self) #对类进行实例化,并返回该实例,用一个变量obj接收
obj.__init__(*args, **kwargs)#和__new__一起用才是真正的构造函数
return obj #返回obj,这个obj其实就是Person类的名称空间
class Person(object,metaclass=Mymeta):
def __init__(self,name):
self.name = name
def __call__(self, *args, **kwargs):
print('我是Person类中的__call__方法')
p = Person('yjy') #我是Mymeta元类中的__call__方法
p() #对象加括号调用Person中的__call__方法,我是Person类中的__call__方法
print(p.name) #yjy
练习:把对象所有的属性都变成私有
class Mymeta(type):
def __call__(self, *args, **kwargs):
print('我是Mymeta元类中的__call__方法')
obj = object.__new__(self)
obj.__init__(*args, **kwargs)
#print(obj.__dict__) {'name': 'yjy'}
obj.__dict__ = {'_%s__%s'%(self.__name__,k): v for k, v in obj.__dict__.items()}
#print(obj.__dict__) {'_Person__name': 'yjy'}
return obj
class Person(object,metaclass=Mymeta):
def __init__(self,name):
self.name = name
def __call__(self, *args, **kwargs):
print('我是Person类中的__call__方法')
p = Person('yjy')
p()
# print(p.name) #AttributeError: 'Person' object has no attribute 'name'
print(p._Person__name) #yjy
五、有了元类之后的属性查找顺序
- 类的属性查找顺序:先从类本身中找--->mro继承关系去父类中找---->去自己定义的元类中找--->type中找--->报错
- 对象的属性查找顺序:先从对象自身找--->类中找--->mro继承关系去父类中找--->报错