目录
一、元类
1. 什么是元类
- 在python中,一切皆对象,用class 关键字定义的类其本身也是一个对象。负责产生该对象的类就是元类。python中,
type
类就是元类,且凡是继承了type
类的类,也是元类。
2. 元类有什么用
-
通过自定义一个元类,来控制类的产生,还可以控制这个类的实例化对象的产生。
-
class People: x = 1 # class People 帮我们完成了 People = type(类名, 类的基类 , 类的名称空间)
3. 内置函数exec
cmd = """
x=1
print('exec函数运行了')
def func(self):
pass
"""
class_dic = {}
# 执行cmd中的代码,然后把产生的名字丢入class_dic字典中
exec(cmd, {}, class_dic) # 第一个参数是字符串python语句,第二个是全局名称空间 ,第三个是局部名称空间
# 打印结果: exec函数运行了
print(class_dic)
# 打印结果: {'x': 1, 'func': <function func at 0x10a0bc048>}
4. 自定义一个元类
class Mymeta(type): # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
def __init__(self, class_name, class_bases, class_dic):
print('self:', self) # self现在是People
print('class_name:', class_name)
print('class_bases:', class_bases)
print('class_dic:', class_dic)
super(Mymeta, self).__init__(class_name, class_bases,
class_dic) # 重用父类type的功能
'''
分析用class自定义类的运行原理(而非元类的的运行原理):
1. 拿到一个字符串格式的类名class_name='People'
2. 拿到一个类的基类们class_bases=(obejct,)
3. 执行类体代码,拿到一个类的名称空间class_dic={...}
4. 调用People=type(class_name,class_bases,class_dic)
'''
5. 元类和普通类的联系
# 连着上面的自定义的元类,之后
class People(object, metaclass=Mymeta): # People=Mymeta(类名,基类们,类的名称空间)
country = 'China'
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print('%s is eating' % self.name)
'''
打印结果:
self: <class '__main__.People'>
class_name: People
class_bases: (<class 'object'>,)
class_dic: {'__module__': '__main__', '__qualname__': 'People', 'country': 'China', '__init__': <function People.__init__ at 0x10a0bcbf8>, 'eat': <function People.eat at 0x10a0bc2f0>}
'''
6. 用元类控制类的创建
- 下面的只是对自定义的普通类的名称空间做一个判断,若是要通过元类修改普通类的名称空间,就要使用
def __new__(cls, class_name , class_bases , class_dic)
,__new__ 和 __init__
接收的参数是一样的,对普通类的名称空间修改之后,在通过return 方法将对象返回出去 。即obj = object.__new__(cls , class_name , class_bases , class_dic) 换行 return obj
。
class Mymeta(type): # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
def __init__(self, class_name, class_bases, 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_bases,
class_dic) # 重用父类的功能
7. __call__
要想让上面6中的obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法:__call__方法,该方法会在调用对象时自动触发。把 __new__ 写在 __call__ 之内即可实现。
8. __new__
-
我们之前说类实例化第一个调用的是__init__,但__init__其实不是实例化一个类的时候第一个被调用 的方法。当使用 Persion(name, age) 这样的表达式来实例化一个类时,最先被调用的方法 其实是 new 方法。
__new__方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。
注意:*new*() 函数只能用于从object继承的新式类。
class A:
pass
class B(A):
def __new__(cls):
print("__new__方法被执行")
return cls.__new__(cls)
def __init__(self):
print("__init__方法被执行")
b = B()
9. 元类控制类的实例化
class Mymeta(type):
def __call__(self, *args, **kwargs):
print(self) # self是People
print(args) # args = ('nick',)
print(kwargs) # kwargs = {'age':18}
# return 123
# 1. 先造出一个People的空对象,申请内存空间
# __new__方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。
obj = self.__new__(self) # 虽然和下面同样是People,但是People没有,找到的__new__是父类的
# 2. 为该对空对象初始化独有的属性
self.__init__(obj, *args, **kwargs)
# 3. 返回一个初始化好的对象
return obj
# People = Mymeta() 此时People 只是一个对象,通过__call__ 可以让他被调用
# People()则会触发__call__
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)
# 在调用Mymeta的__call__的时候,首先会找自己(如下函数)的,自己的没有才会找父类的
# def __new__(cls, *args, **kwargs):
# # print(cls) # cls是People
# # cls.__new__(cls) # 错误,无限死循环,自己找自己的,会无限递归
# obj = super(People, cls).__new__(cls) # 使用父类的,则是去父类中找__new__
# return obj
10. 自定义元类后的类的继承顺序
-
查找顺序:先对象自己——》实例化对象的类 ——》这个类的父类(多父类时按父类的继承顺序 ,新式类按照广度优先)——》object——》自定义的元类——》type
-
注意:所有类(包括type)都继承了object类。即所有类中
__new__
都是最终由object中的__new__
完成的。