前言
打算写一系列文章来记录自己学习 Python 3 的点滴;本章主要介绍 Python 面向对象编程中有关元类的相关内容;
本文为作者的原创作品,转载需注明出处;
type
正如之前的文章中,查看类的类型 以及 查看实例的类型 所描述的那样,type()
可以查看对象的类型,不管是类还是实例;不过type()
还有一个更为强大的功能,那就是在运行过程中动态的创建出一个类class
对象;
这也就是静态语言和动态语言最大区别所在的地方,静态语言的类在编译的时候就创建好了,动态语言,是可以在程序的执行过程之中,边执行边创建一个新的类出来;(不过这个边界也越来越模糊了,典型的静态语言 Java,在动态执行的过程中,也可以通过 ClassLoader 类加载器在程序执行的过程当中,通过字节码的方式,动态的加载(创建)一个类 Class 对象 );
那么,下面,笔者就带领大家来看看 Python 是如何通过type()
在程序的运行时动态的创建出一个 Class 对象的;
先定义出一个函数
1
2def fn(self, name='world'): # 先定义函数
'Hello, %s.' % name) print(通过
type()
生成一个新的 Class 对象1
2
3
4'Hello', (object,), dict(hello=fn)) Hello = type(
"everyone") h = Hello(
h.hello()
Hello, everyone.可见,我们通过
type()
动态的构造出来了一个 class 对象 Hello;在通过该对象实例化了一个对象 h,调用实例方法 hello() 得到了对应的输出;
那么type()
函数所对应的参数是什么意义呢?type()
函数需要输入三个位置参数,分别对应的是,
- 类名: str 类型
所以上述所动态创建出来的 class 的类名为Hello
- 所继承的父类的集合: tuple 类型
注意,上述例子中 (object,) 的写法表示当该 tuple 有且只有一个对象的时候的写法; - 创建 class 的函数: dict 类型
第三个参数接收的是一个 dict 对象,其中的键值对的键
表示的是方法名
,值
表示方法的引用
;
metaclass
先来看一个例子,首先定义一个 ListMetaclass,
1 | class ListMetaclass(type): |
然后,我们定义一个 MyList 对象,该对象继承自两个父类,一个是 List 还有一个就是 metaclass
1 | class MyList(list, metaclass=ListMetaclass): |
OK,现在大家都还不清楚 metaclass 到底能干嘛,看看下面的这个执行的例子,
首先,我们初始化 MyList class,
1 | >>> class MyList(list, metaclass=ListMetaclass): |
可见,当 Python 解释器在解释执行到 MyList 的时候,调用了 ListMetaclass 中的 __new__ 方法,该方法内部,通过 lambda 为 MyList class 添加了一个新的方法 add(self, value),并将该方法通过 type.__new__() 方法注入;回顾一下 type 的作用,就是在程序动态的执行过程当中,生成一个新的 class 并返回,该 class 就是最终通过 Python 解释器所得到的 MyList class;突然让我想到了 Java 的字节码技术,Java 可以通过修改和添加字节码的方式在编译期、执行期通过修改 class 的字节码已达到动态的修改 class 的目的,比较典型的就是 ASM 字节码技术;所以,Python 中的 ListMetaclass 可类比为 Java 的字节码技术,在运行期,动态的修改 class 的属性和功能;这也就是 Python 的 Metaclass 的魔力所在了;最后,注意构建 ListMetaclass 的时候参数 attrs 的输出,里面会包含 MyList 的类属性,‘description’: ‘My List’;这一点尤为
重要,提供了将当前类的定义当做模板来构建顶层设计的可能,正式这一点,使得 metadata 大量的使用在了 Python 的 ORM 领域;
然后,我们测试一下被 ListMetaclass 所修饰的 MyList 对象;
1 | l = MyList() |
我们知道,原生的 list 对象是不包含 add 方法的,这样,我们通过元类 metaclass 的方式动态的为 list 的子类 MyList 扩展出了 add 方法;
读者会问,弄得这么复杂干嘛呢?直接在 MyList 对象中定义一个 add() 方法不就 OK 了?是的,上面的这个 case 的确如此了;但是,有没有想过,如果我有几十上百个拥有同种类型的 class,典型的就是 ORM 中的 Object class,每一个 class 都需要实现增、删、查、改的方法;所以 metaclass 提供了对同类 class 的一系列公用方法的抽象;对了,你又会问了,写一个抽象类不就 Ok 了吗?是的,那也是一种实现方法,而且在 Java 中屡见不鲜,而且有一种专门的模式,Template 模式就是由此而来的;不过,Template 或者抽象类的方式有一个弊端,就是抽象类不知道子类内部的元素的定义,不能讲子类的定义当做输入元素来作为顶层设计的模板,而这一点,只有 metadata 可以做到;