元类
1. 什么是元类
- 通过实例化产生类的类,称之为元类,元类实例化的结果就是
class
产生的类
- 在Python中一切皆对象,那么用
class
关键字定义的类,本身也是对象
- 产生该对象的类就称为元类,也可以简称为类的类
2. 为什么要有元类
- 元类是用来产生类的,元类和自定义元类,可以控制类的产生过程和对象的产生过程
3. 创造名称空间---内置函数exec
exec
的作用,是将字符串中的代码,提取出来执行,并产生名称空间
- 语法
exec(字符串,全局名称空间字典,局部名称空间字典)
- 全局名称空间为
{}
目前不用考虑,只是占位
- 执行字符串中的代码,产生的名字,都放到了局部名称空间字典中
- 这一步,就模拟了类执行类体代码,产生名称空间的过程
cmd='''
x=1
y=1
print('--------------->')
'''
local_dic={}
exec(cmd,{},local_dic)
print(local_dic)
===============================
--------------->
{'x': 1, 'y': 1}
cmd='''
x=1
def func(self):
pass
'''
class_dic={}
exec(cmd,{},class_dic)
print(class_dic)
========================================================
{'x': 1, 'func': <function func at 0x000001B4C14E1E18>}
4. 创建类的原理过程
- 创造类的
3
个要素:类名
、父类(基类)
、类的名称空间
# 准备创造类的三要素:
class_name='People' # 类名
class_bases=(object,) # 父类(基类)
class_dic={} # 类的名称空间
class_body='''
country='China'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating' %self.name)
'''
# 创造名称空间
exec(class_body,{},class_dic)
# 三要素:
print(class_name)
print(class_bases)
print(class_dic)
================================================================
People
(<class 'object'>,)
{'country': 'China', '__init__': <function __init__ at 0x000002446E501E18>, 'eat': <function eat at 0x000002446E6DD158>}
# 最后调用元类,即元类的实例化,产生了一个类
People=type(class_name,class_bases,class_dic)
print(People)
====================================================
<class '__main__.People'>
5. 创建类的方法一:使用class关键字创建
- 如果说类也是对象,那么用
class
关键字去创建类的过程就是实例化的过程
- 该实例化的目的是为了得到一个类,默认调用的就是元类
class People:
country='China'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating' %self.name)
print(type(People))
===============================================
# 默认的元类,是type,type()传入的参数是造类的关键
<class 'type'>
6. 创建类的方法二:自定义元类
class Mymeta(type): # 必须继承type类,才能自定义元类,否则就是一个普通的自定义类
# 调用元类实例化类
def __init__(self,class_name,class_base,class_dic): # 初始化类
print(self) # self是类名People
print(class_name)
print(class_base)
print(class_dic)
# 最好写上,用来重用父类type元类的其他功能
super(Mymeta, self).__init__(class_name,class_base,class_dic)
# class就是结合3个元素的3件事
class People(object,metaclass=Mymeta): # 基类 + metaclass指定元类名
country='China'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating' %self.name)
====================================================================
<class '__main__.People'>
People
(<class 'object'>,)
{'__module__': '__main__', '__qualname__': 'People', 'country': 'China', '__init__': <function People.__init__ at 0x000001E56A1FD1E0>, 'eat': <function People.eat at 0x000001E56A1FD268>}
7. 自定义元类的应用
- 自定义元类,控制类的创建过程,类的产生过程其实就是元类的调用过程
① 要求创建的类必须加文档注释
class Mymeta(type):
def __init__(self,class_name,class_base,class_dic):
# 定制创建的类中必须要有文档注释
if class_dic.get('__doc__') is None or len(class_dic.get('__doc__').strip()) == 0:
raise TypeError('类中必须有文档注释,并且注释不能为空')
super(Mymeta, self).__init__(class_name,class_base,class_dic)
class People(object,metaclass=Mymeta):
'''这是一个注释''' # 如果不写就会报错
country='China'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating' %self.name)
=======================================================================
TypeError: 类中必须有文档注释,并且注释不能为空
② 要求创建的类名开头必须大写
class Mymeta(type):
def __init__(self,class_name,class_base,class_dic):
if class_dic.get('__doc__') is None or len(class_dic.get('__doc__').strip()) == 0:
raise TypeError('类中必须有文档注释,并且注释不能为空')
# 定制类名首字母必须大写
if not class_name.istitle():
raise TypeError('首字母必须大写')
super(Mymeta, self).__init__(class_name,class_base,class_dic)
class people(object,metaclass=Mymeta):
'''注释文档'''
country='China'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating' %self.name)
=======================================================================
TypeError: 首字母必须大写
8. __call__
- 将对象变为可调用的对象,就需要在该对象的类中定义
__call__
方法,该方法会在调用对象时,自动触发
class Foo:
pass
obj=Foo()
obj()
=======================================
# 默认对象不能调用
TypeError: 'Foo' object is not callable
class Foo:
def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
obj=Foo()
obj(1,2,3,x=1,y=2)
=============================================
<__main__.Foo object at 0x000001C8AB52C358>
(1, 2, 3)
{'x': 1, 'y': 2}
- 通过自定义元类,可以控制类的调用过程,即控制类的实例化过程
- 如果想让类可以调用,就需要在自定义的元类中添加
__call__
- 即当类被调用时,就自动触发了元类中的
__call__
- 以前是需要
obj=People('xut',18)
的形式,现在可以直接People()
调用
- 调类和调对象就是调
__call__
的方法,并且可以拿到这个方法的返回值,不写默认是None
- 如何控制
__call__
,自定义元类的call
class Mymeta(type):
def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
class People(object,metaclass=Mymeta):
country='China'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating' %self.name)
# People()
obj=People('xut',18)
print(obj)
=======================================================
<class '__main__.People'>
('xut', 18)
{}
None # 调__call__的方法默认返回None
- 说明:调对象(这里调的是类,People('xut',18))就是调
__call__
的方法,__call__
返回值,赋值给一个变量(给obj
)
- 如果类是对象,调类就是调元类中的
__call__
class Mymeta(type):
def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
# 规定了返回值
return 123
class People(object,metaclass=Mymeta):
country='China'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating' %self.name)
obj=People('xut',18)
print(obj)
===============================================
<class '__main__.People'>
('xut', 18)
{}
123
9. __new__
class Mymeta(type):
def __call__(self, *args, **kwargs):
# 1. 用__new__创造一个People的空对象
self.__new__(self) # 这里的self是People
# 2. 给这个空对象,初始化独有的属性
print(args,kwargs)
class People(object,metaclass=Mymeta):
country='China'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating' %self.name)
obj1=People('xut',age=18)
obj2=People('xdw',age=18)
print(obj1)
print(obj2)
============================================================
('xut',) {'age': 18}
('xdw',) {'age': 18}
None
None
People('xut',age=20)
============================================================
('xut',) {'age': 20}
class Mymeta(type):
def __call__(self, *args, **kwargs):
# 1. 先造出一个People的空对象
obj=self.__new__(self)
# 2. 为该空对象初始化独有的属性
self.__init__(obj,*args,**kwargs)
# 3. 返回一个初始化好的对象
return obj
class People(object,metaclass=Mymeta):
country='China'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating' %self.name)
obj=People('xut',age=18)
print(obj.__dict__)
print(obj.name)
obj.eat()
==============================================
{'name': 'xut', 'age': 18}
xut
xut is eating
class Mymeta(type):
def __call__(self, *args, **kwargs):
# 1. 先造出一个People的空对象
obj=self.__new__(self)
# 2. 为该空对象初始化独有的属性
self.__init__(obj,*args,**kwargs)
# 3. 返回一个初始化好的对象
return obj
class People(object,metaclass=Mymeta):
country='China'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating' %self.name)
def __new__(cls, *args, **kwargs):
print(cls)
obj=super().__new__(cls)
return obj
obj=People('xut',age=18)
print(obj.__dict__)
print(obj.name)
obj.eat()
=============================================
<class '__main__.People'>
{'name': 'xut', 'age': 18}
xut
xut is eating