元编程,一个听起来特别酷的词,强大的Lisp在这方面是好手,对于Python,尽管没有完善的元编程范式,一些天才的开发者还是创作了很多元编程的魔法。Django的ORM本文来源gaodaimacom搞#^代%!码&网*就是元编程的一个很好的例子。
本篇的概念和例子皆在Python3.6环境下
一切都是对象
Python里一切都是对象(object
),基本数据类型,如数字,字串,函数都是对象。对象可以由类(class
)进行创建。既然一切都是对象,那么类是对象吗?
是的,类也是对象,那么又是谁创造了类呢?答案也很简单,也是类,一个能创作类的类,就像上帝一样,开启了万物之始。这样的类,称之为元类(classmeta
)。
类的定义
对象是通过类创建的,这个很好理解。例如下面的代码:
class Bar(object): pass bar = Bar() print(bar, bar.__class__) # <__main__.Bar object at 0x101eb4630> <class '__main__.Bar'> print(Bar, Bar.__class__) # <class '__main__.Bar'> <class 'type'>
可以看见对象 bar 是类 Bar 创建的实例。然而 Bar,看起来却是由一个叫 type 的类创建的实例。即 bar <-- Bar < -- type
。
上面的例子,对象是动态创建的,类则是通过关键字 class 声明定义的。class关键字背后的玄机是什么呢?
实际上,class Bar(object)
这样的代码,等价于 Bar = type('Bar', (objects, ), {})
即类 type 通过实例化创建了它的对象 Bar,而这个 Bar 恰恰是一个类。这样能创建类的类,就是 Python 的元类。
从创建 Bar 的代码上来看,元类 type 的 __init__
方法有3个参数,
- 第一个是创建的类的名字
- 第二个是其继承父类的元类列表,
- 最后就是一个属性字典,即该类所具有的属性。
type 元类
type是小写,因而很容易误以为它是一个函数。通过help(type)可以看到它的定义如下:
class type(object): """ type(object_or_name, bases, dict) type(object) -> the object's type type(name, bases, dict) -> a new type """ def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__ """ type(object_or_name, bases, dict) type(object) -> the object's type type(name, bases, dict) -> a new type # (copied from class doc) """ pass @staticmethod # known case of __new__ def __new__(*args, **kwargs): # real signature unknown """ Create and return a new object. See help(type) for accurate signature. """ pass
如前所述,__init__
方法接受三个参数,type 实例化的过程,会创建一个新的类。创建类的代码来自 __new__
方法,它的参数其实和 __init__
,一样。至于它们之间有什么关系,后面再做介绍。目前只要知道,当调用 type 进行实例化的时候,会先自动调用 __new__
方法,然后再接着调用 __init__
方法,在类外面来看,最终会实例化一个对象,这个对象是一个类。
从 type 的定义来看,它继承 object,Python3的所有类,都继承来着 object,类type 也是 object 的实例,令人奇怪的是,object 既是类也是对象,它也是由 type实例化而来。有一种鸡生蛋,蛋生鸡的悖论。暂且先不管,只要知道所有类的顶级继承来自 object 就好。
自定义元类
既然元类可以创建类,那么自定义元类就很简单了,直接继承类 type 即可。先看下面一个例子:
class MyType(type): pass class Bar(object, metaclass=MyType): pass print(MyType, MyType.__class__) # <class '__main__.MyType'> <class 'type'> print(Bar, Bar.__class__) # <class '__main__.Bar'> <class '__main__.MyType'>