一、什么是元类
1 |
|
这么说可能不太好理解,下面我们来解释下上面这句话:
1 2 3 4 5 |
|
所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化),python内置了一个方法可以看到类对象的类,就是type,:
查看对象的类型:
查看类的类型
结果为<class 'type'>,证明是调用了type这个元类而产生的StanfordTeacher,即默认的元类为type,是不是有些惊讶
于是我们可以推导出:
二、class关键字创建类的流程分析
上文我们基于python中一切皆为对象的概念分析出:我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type
class关键字在帮我们创建类时,必然帮我们调用了元类StanfordTeacher=type(...),那调用type时传入的参数是什么呢?必然是类的关键组成部分,一个类有三大组成部分,分别是:
1 2 3 4 5 |
|
调用type时会依次传入以上三个参数
type可以接受一个类的描述作为参数,然后返回一个类。type可以像这样工作:
type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
比如下面的代码:
可以手动像这样创建:
那么class关键字底层的做了哪些事?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
综上,class关键字帮我们创建一个类应该细分为以下四个过程
三、到底什么是元类
元类就是用来创建类的“东西”。你创建类就是为了创建类的实例对象,不是吗?但是我们已经学习到了Python中的类也是对象。
元类就是用来创建这些类(对象)的,元类就是类的类,你可以这样理解为:
1 2 |
|
你已经看到了type可以让你像这样做:
1 |
|
这是因为函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。type就是创建类对象的类。你可以通过检查__class__属性来看到这一点。Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。
四、 自定义元类控制类的创建
一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程,即
1 2 3 4 5 |
|
于是我们可以自定义元类来控制StanfordTeacher类的产生:如下示例
1.自定义元类来控制StanfordTeacher类的产生
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
自定义元类来控制StanfordTeacher类的调用:
储备知识:__call__
1 2 3 4 5 6 7 8 9 10 |
|
由上例得知,调用一个对象,就是触发对象所在类中的__call__方法的执行,如果把StanfordTeacher也当做一个对象,那么在StanfordTeacher这个对象的类中也必然存在一个__call__方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
默认地,调用t1=StanfordTeacher('lili',18)会做三件事:
1 2 3 4 5 |
|
对应着,StanfordTeacher类中的__call__方法也应该做这三件事:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
上例的__call__相当于一个模板,我们可以在该基础上改写__call__的逻辑从而控制调用StanfordTeacher的过程,比如将StanfordTeacher的对象的所有属性都变成私有的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
|
最后说明一点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
五、元类冲突
假如有两个不同的元类,要生成一个继承这两个类的子类,会产生什么情况呢?
这时会报错, 元类冲突。
我们需要手动构造新的子类元类,让新的子类元类继承自A和B的元类:
六、元类的应用
应用元类之前我们首先要知道使用元类编程的缺点:
1 2 3 |
|
其实在开头引用TimPeters的话就说明,不要随意在生产代码中使用元类,而且现有的编码规范也极不推荐使用。
就元类本身而言,它的作用是:
1 2 3 |
|
使用元类还是有一些好处的:
1 2 3 4 |
|
七、在Python当中,__call__,__new__,__init__三者之间的关系
在类实例化的过程当中,哪个对象加()就寻找产生这个对象的类的__call__方法,只要是__call__方法,一定会做三件事情:
1 2 3 4 5 |
|
注意:__new__更像是其他语言当中的构造函数,必须有返回值,返回值实例化的对象,__init__只是初始化构造函数,必须没有返回值,仅仅只是初始化功能,并不能new创建对象.
也就是说,一个类在实例化的时候实际上是做了三件事情:
1 2 3 4 5 6 7 |
|
类在实例化对象的时候函数的调用顺序依次是:
1 |
|
八、练习:
1、在元类中控制把自定义类的数据属性都变成大写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
2、在元类中控制自定义的类不使用__init__方法完成以下
1.元类帮其完成创建对象,以及初始化操作;
2.要求实例化时传参必须为关键字形式,否则抛出异常TypeError: must use keyword argument
3.key作为用户自定义类产生对象的属性,且所有属性变成大写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
3、在元类中控制自定义的类产生的对象相关的属性全部为隐藏属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
4、基于元类实现单例模式
1 2 3 4 5 |
|
方式一:定义一个类方法实现单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
方式二:定制元类实现单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
方式三:定义一个装饰器实现单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|