您的位置:首页 > 编程语言 > Python开发

metaclass in python

2008-03-12 11:23 471 查看
python 的东西虽然概念上容易理解 ,但是实际用起来却也不都是那么容易的。这个 metaclass 就是典型一例。虽然早知道了它是什么,不过要说出它具体能干点啥,一时半会还真想不起来。
先看看官方文档中的定义吧:
__metaclass__ This variable can be any callable accepting arguments for
name
,
bases
, and
dict
. Upon class creation, the callable is used instead of the built-in type(). New in version 2.2. The appropriate metaclass is determined by the following precedence rules:

If
dict['__metaclass__']
exists, it is used.

Otherwise, if there is at least one base class, its metaclass is used (this looks for a __class__ attribute first and if not found, uses its type).

Otherwise, if a global variable named __metaclass__ exists, it is used.

Otherwise, the old-style, classic metaclass (types.ClassType) is used.

The potential uses for metaclasses are boundless. Some ideas that have been explored including logging, interface checking, automatic delegation, automatic property creation, proxies, frameworks, and automatic resource locking/synchronization.

看完这个似乎又能理清了一点东西:所谓 new-style class 和 old-style class 最根本的区别其实正在于它们的 metaclass 一个是 type,一个是 types.ClassType,所以只要一个 class 继承自 object 而又没有指定自己的 __metaclass__ 的话,其 metaclass 就会自动使用基类 object 的 __metaclass__,但是 object 却没有 __metaclass__,那就使用 type(object),也就是 type 了!

然后再 google 一下(这次是要 google code 一下了),看看实际生活中的代码都用它来干了点啥。不搜不知道,这一搜还真发现不少有意思的结果:
第一条[地址],是twisted里的代码
__metaclass__ = type

真有创意!根据上面定义中 __metaclass__ 查找顺序,在使用 old-style class 的 metaclass (也就是types.ClassType)之前会找一下全局变量 __metaclass__ ,而上面代码中通过定义模块全局变量 __metaclass__ ,使得模块中原来的 old-style class 立刻变成了 new-style class。使用这种方法来将 old-style class 升级为 new-style class,确实省了不少代码,毕竟将来的 python 中将会只存在 new-style class ,那时候便可放心地将这一句去掉了。聪明!(不过这样会导致查找 metaclass 的过程要多了几个步骤,效率上可能就 ... )

第二条[地址],从这个例子的路径看它是在 python25/tools/ 下面,不过翻了一下自己python25 安装目录下却没有发现这些代码,郁闷!
在这个例子中,__metaclass__ 被当成了一种快捷定义 class 方法的方式了。
我们知道在 class 中定义的方法其实默认都是 instance 方法,要使它们成为 class 方法需要使用 classmethod 进行装饰,对于有大量 class 方法的类来说,这的确是有点小麻烦。
既然 class 中定义的方法是 class 的 instance 方法,那么在 type 中定义的方法岂不就是 type 的 instance 方法,也就正好是 class 方法了?
这个例子便是利用这一点,通过继承 type,添加 type 的 instance 方法,也就是添加了 class 方法了。好聪明啊,呵呵 ;-)

第三条的用法与第一条类似,第四条[地址]来自传说中的 psyco ! 一看到这个名字差点没有勇气继续看下去了,不过还好,这个 metaclass 其实并不复杂,它的作用就是在 class 创建之后自动将它或 __psyco_bind__ 中指定的属性 bind 到 psyco,具体 bind 干了什么事情就只有对 psyco 有研究的兄弟来解答了,不过估计这样就可以进行某种特别的优化了。也许你要说了:这个工作也可以在 class 的 __init__ 或是 __new__ 方法里做啊。但是放在 __init__ 里面做的话你就需要其他的 class 都来继承你这个基类了,而使用 __new__ 的问题是它可以被子类 override 掉。
使用 metaclass 的另一个好处是,你可以神不知鬼不觉地修改 class 的创建过程,比如
这个例子里面,在模块中定义好 __metaclass__ 全局变量,那当其他的代码 from module import * 的时候,该 __metaclass__ 的定义就会自动作用于其后所定义的所有 class 了。

第五条和第八条的用法也和第一条类似,第六条貌似只是在使用 __metaclass__ 的情况下对 pickle 进行测试,没有什么特别的。至于第七条。。。

现在我们知道了,metaclass 生 class,class 生 instance。
但是 metaclass 还可以有它的 metametaclass,metametaclass 还可以有... 如果反复,永无止境。这样想起来,脑袋就有点晕了。
其实在 python 中万物皆对象而已,所有对象皆有其类型,对象的类型也还是对象!而类型对象的类型就是它自己。
而前面说过的所谓 instance、class、metaclass 等东西,都只是不同种类的对象而已。

判断对象是什么对象的唯一方法就是通过其提供的“接口”,这就是所谓的 duck type!只要一个对象实现了成为一个 class 所需的接口,它就是 class !metaclass 亦然。
那么我们不如先来讨论讨论 metaclass 的接口吧,看看究竟需要实现哪些接口能使一个对象成为一个 metaclass。
先来看一些等价关系:
class Temp(object):
__metaclass__ = Meta
a = 1
def __init__(self, a):
self.a = a

上面代码其实等价于:
Temp = Meta('Temp', (object,),
{'a':1, '__module__':'current module name', '__metaclass__':the object Meta, '__init__':function object __init__})

(class的语法原来只是个语法糖而已,汗!)
由此可见 Meta 首先应该是个 callable,并且应该接受如上所示的三个参数。
t = Temp(2) # 构建 Temp 的 instance

从这一句我们可以看出 Temp 也应该是个 callable 对象。
而我们知道 Temp 对象其实是调用 Meta 所返回的,也就是说 Meta 这个 callable 对象返回的还应该是一个 callable 对象。
典型地,如果 Meta 是一个 class,意味着它应该实现一个 __call__ 方法。这样的话,那么上面那句就可以等价为:
t = Temp.__call__(2)

上面说的这几点基本上可以作为判断一个对象能否成为 metaclass 的标准了:一个接受三个参数并返回另一个 callable 对象的 callable 对象!

不急,在继续分析之前我们不妨利用刚才发现的这一点搞点小怪先,呵呵。
def Meta(name, bases, attrs):
def _class(a):
return a
return _class

class Temp(object):
'''
>>> Temp(1)
1
>>> Temp('hello')
'hello'
'''
__metaclass__ = Meta

继续分析,虽然可以像上面那样恶搞,不过要想写个有点实际用处的 metaclass ,还是通过 class 来实现比较方便。
最典型的方法便是直接继承 type 了,毕竟那是所有 new-style class 的 metaclass,在 python3000 里就要成为所有 class 默认的 metaclass 了。
可以说大部分 metaclass 的实现都是这么做的,不过下面要分析的这一例却是个例外,虽然不像上面我们写的那个 metaclass 那么奇怪,不过分析起来也不是那么容易的。

不过我们还需要继续澄清一些事实,先看这个例子:
class Temp(object):
@staticmethod  # 这一句可以忽略,不管有没有这句,__new__ 都是静态方法
def __new__(cls, a):
return object.__new__(cls, a)
def __init__(self, a):
self.a = a
def __getattribute__(self, name):
return super(Temp, self).__getattribute__(name)
def __getattr__(self, name):
return super(Temp, self).__getattr__(name)

t = Temp(2)
print t.a

在默认的 metaclass type 的实现中,上面这句,也就是 type 的 __call__ 方法,其实是分以下两步完成的:
t = Temp.__new__(Temp, 2) # 调用 staticmethod __new__,创建 instance
t.__init__(2) # 调用该 instance 的构造函数,初始化 instance

另外,既然 Temp 实现了 __getattribute__, t.a 实际上等价于:
try:
t.__getattribute__(self, 'a')
except AttributeError:
t.__getattr__(self, 'a')

到这里基本上一些概念问题已经搞清楚了,下一篇终于可以正式开始研究这里的代码
http://www.python.org/download/releases/2.2.3/descrintro/ http://www.python.org/download/releases/2.3/mro/ http://wiki.woodpecker.org.cn/moin/PyNewStyleClass http://wiki.woodpecker.org.cn/moin/MetaClassInPython
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息