目录
1. 元类
1.1 什么是元类
- 在python中,一切皆对象,那么我们用class关键字定义的类也是一个对象,而负责产生该对象的类称之为元类
1.2 为什么用元类
- 元类是负责产生类的,所以我们可以通过元类控制类的产生过程,把一些不符合的筛选掉,同时还可以通过元类里的
__call__
方法控制对象的产生
1.3 类的创建
1.3.1 正常定义一个类
# 定义一个类
class Foo(object): # python3中默认继承object类
count = 0
def __init__(self,name,age):
self.name = name
self.age = age
@property
def func(self):
print('from func')
f1 = Foo('hades',27)
print(f1.__dict__)
f1.func
{'name': 'hades', 'age': 27}
from func
1.3.2 内置函数exec
exec会把字符串按照python语法解析成代码,把解析完的代码放入字典中
- 语法:exec(str:-->符合Python语法规范的字符串, {}:-->Python中的内置函数, 变量名:-->dict)
s = '''
count = 0
def __init__(self,name,age):
self.name = name
self.age = age
def func(self):
print('from func')
'''
dic = dict()
exec(s,{},dic)
print(dic)
{'count': 0, '__init__': <function __init__ at 0x0000023CB5544C80>, 'func': <function func at 0x0000023CB55FD378>}
1.3.3 type创建类
1.3.3.1 类的三大组成
- 类名
- 基类
- 类的名称空间
# 1. 类名
class_name = 'Foo'
# 2. 类体代码(需要按照python语法规则)
class_body = '''
count = 0
def __init__(self,name,age):
self.name = name
self.age = age
@property
def func(self):
print('from func')
'''
# 3. 基类(父类)
class_bases =(object,) # 必须加逗号,形成一个元组
class_dict = dict()
exec(class_body, {}, class_dict) # 产生类体名称空间
print(class_dict)
{'count': 0, '__init__': <function __init__ at 0x0000023CB56842F0>, 'func': <property object at 0x0000023CB56773B8>}
1.3.3.2 type关键字生成类
- 语法:type(类名:-->str,基类:-->tuple, 类的名称空间:-->dict)
Foo1 = type(class_name, class_bases, class_dict)
print(Foo1)
<class '__main__.Foo'>
f2 = Foo1('hades', 27)
print(f2.__dict__)
f2.func
{'name': 'hades', 'age': 27}
from func
这样我们就生成了一个类
1.3.4 自定义元类控制类的创建
- 使用自定义个元类
class Mymeta(type): # 只有继承了type类才能称之为一个元类,否则就是一个很普通的类
def __init__(self, class_name, class_bases, class_dict):
print(class_name)
print(class_bases)
print(class_dict)
super().__init__( class_name, class_bases, class_dict) # 使用父类功能生成一个类对象
print('创建类成功了')
-
分析用class自定义类的运行原理(而非元类的的运行原理):
1.拿到一个字符串格式的类名class_name = 'Foo'
2.拿到一个类的基类们class_bases = (obejct,)
3.执行类体代码,拿到一个类的名称空间class_dict={...}
4.调用Foo1 = type(class_name, class_bases, class_dict)
class Foo(metaclass=Mymeta): # python3中默认继承object类
count = 0
def __init__(self,name,age):
self.name = name
self.age = age
@property
def func(self):
print(f'name "{self.name}" from func')
Foo
()
{'__module__': '__main__', '__qualname__': 'Foo', 'count': 0, '__init__': <function Foo.__init__ at 0x0000023CB5684D08>, 'func': <property object at 0x0000023CB5690368>}
创建类成功了
f1 = Foo('Bonnie',16)
f1.func
name "Bonnie" from func
1.4 应用
-
自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程
-
我们可以控制类必须有文档,类名首字母要大写,可以使用如下的方式实现
class Mymeta(type):
def __init__(self, class_name, class_bases, class_dict):
if not class_name.istitle():
raise TypeError('类名首字母要进行大写')
if (not class_dict.get('__doc__')) or len(class_dict['__doc__']) == 0:
raise TypeError("必须有文档注释")
super().__init__( class_name, class_bases, class_dict)
# 类名首字母没有大写
try:
class foo(metaclass=Mymeta):
def __init__(self,name,age):
self.name = name
self.age = age
except Exception as e:
print(e)
类名首字母要进行大写
# 没有注释
try:
class Foo(metaclass=Mymeta):
def __init__(self,name,age):
self.name = name
self.age = age
except Exception as e:
print(e)
必须有文档注释
try:
class Foo(metaclass=Mymeta):
"""测试文件"""
def __init__(self,name,age):
self.name = name
self.age = age
f1 = Foo('hades',20)
print(f1.__dict__) # {'name': 'hades', 'age': 20}
except Exception as e:
print(e)
{'name': 'hades', 'age': 20}
1.5 自定义元类控制类的实例化
可以通过__call__
方法进行实现
class Mymeta(type):
def __init__(self, class_name, class_bases, class_dict):
if not class_name.istitle():
raise TypeError('类名首字母要进行大写')
if (not class_dict.get('__doc__')) or len(class_dict['__doc__']) == 0:
raise TypeError("必须有文档注释")
super().__init__( class_name, class_bases, class_dict)
def __call__(self,*args,**kwargs):
print(self)
print(args)
print(kwargs)
obj = self.__new__(self)
self.__init__(obj,*args,**kwargs)
print(obj)
return obj
class Foo(metaclass=Mymeta):
"""测试文件"""
def __init__(self,name,age):
self.name = name
self.age = age
f1 = Foo('Bonnie',18)
print(f1)
print(f1.__dict__)
<class '__main__.Foo'>
('Bonnie', 18)
{}
<__main__.Foo object at 0x0000023CB56B4048>
<__main__.Foo object at 0x0000023CB56B4048>
{'name': 'Bonnie', 'age': 18}
-
类的调用,即类实例化就是元类的调用过程,可以通过元类Mymeta的__call__方法控制
-
分析:调用Pepole的目的
-
先造出一个People的空对象
-
为该对空对象初始化独有的属性
-
返回一个初始化好的对象
-
1.6 自定制元类后类的继承顺序
- 先对象本身-->类-->父类-->父类的父类-->object-->自己定制的元类-->type