真的,它并非巫术。
原文地址:http://www.voidspace.org.uk/python/articles/five-minutes.shtml
日期:16 September, 2008.
译者:赖勇浩(http://blog.csdn.net/lanphaday)
“元类的魔幻变化比 99% 的用户所担心的更多,当你搞不懂是否真的需要用它的时候,就是不需要。”
—Tim Peters
本文源于在 PyCon UK 2008 上的一个快速演讲。
元类被称为 Python 中的“深奥的巫术”。尽管你需要用到它的地方极少(除非你基于 zope 编程),可事实上它的基础理论其实令人惊讶地易懂。
一切皆对象
以前,术语 type 用于内置类型,而术语 class 用于用户定义的类,但自 Pythoon 2.2 以来“class”和“type”本质上并无不同。
对于旧风格(old-style)类的类型是 types.ClassType。
真的,这是真的
Python 2.5.1 (r251:54869, Apr 18 2007, 22:08:04)
>>> class Something(object):
... pass
...
>>> Something
<class '__main__.Something'>
>>> type(Something)
<type 'type'>
从这里可以看出在交互式解释器中创建的类是一个 first class 的对象。
类的类是……
它的元类……
就像对象是类的实例一样,类是它的元类的实例。
调用元类可以创建类。
确切来说,Python 中的其它对象也是如此。
因此当你创建一个类时……
解释器会调用元类来生成它……
定义一个继承自 object 的普通类意味着调用 type 来创建它:
>>> help(type)
Help on class type in module __builtin__:
class type(object)
| type(object) -> the object's type
| type(name, bases, dict) -> a new type
type 的第二种用法尤为重要。当 Python 解释器在执行一条类定义语句时(如例子中最初的两行代码之后),它会用下面的参数调用 type:
简单模拟
>>> def __init__(self):
... self.message = 'Hello World'
...
>>> def say_hello(self):
... print self.message
...
>>> attrs = {'__init__': __init__, 'say_hello': say_hello}
>>> bases = (object,)
>>> Hello = type('Hello', bases, attrs)
>>> Hello
<class '__main__.Hello'>
>>> h = Hello()
>>> h.say_hello()
Hello World
以上代码创建了类属性的字典,然后调用 type 来创建了名为 Hello 的类。
__metaclass__ 的魔法
只要在类定义中把 __metaclass__ 设置为任意有着与 type 相同参数的可调用对象,就能够提供自定义的元类。
通常使用从 type 继承的方法:
class PointlessMetaclass(type):
def __new__(meta, name, bases, attrs):
# do stuff...
return type.__new__(meta, name, bases, attrs)
重要的是在 __new__ 方法中我们能够读取或改变传入的用以创建新类的参数。从而能够内省属性字典和改动、增加或者删除成员。
尽管当实例化一个类时这两个函数都会被调用,但覆盖 __new__ 比 __init__ 更为重要。__init__ 初始化一个实例,而 __new__ 的职责是创建它。因此如果元类用以自定义类的创建,就需要覆盖 type 的 __new__。
使用新类而非仅仅提供工厂函数的原因在于如果使用工厂函数(那样只是调用 type)的话元类不会被继承。
In Action...
>>> class WhizzBang(object):
... __metaclass__ = PointlessMetaclass
...
>>> WhizzBang
<class '__main__.WhizzBang'>
>>> type(WhizzBang)
<class '__main__.PointlessMetaClass'>
WhizzBang 是一个类,但它现在已经不是 type 的实例,而是我们自定义的元类的实例了……
这有什么用?
很好的问题,元类将用在创建使用了它的新类时调用,这里是一些关于这样做的好处的观点:
最重要之处在于元类中是在最后对 type 的调用时才真正创建类,所以可以自由地随你喜欢地改变属性字典(以及名称和元组形式的基类序列)。
一些流行的 Python ORM(Object Relational Mappers(对象关系影射),用以和数据库协同工作)也如此使用元类。
哦,还有因为元类是继承的,所以你能够提供一个使用了你的元类的基类,而继承自它的子类就无需显式声明它了。
但是……
我曾未需要使用它来编写代码……(我们用它来剖分,也在 Ironclad 项目广泛应用它,但我不编写这些)。
还有,这一切只适用于 Python 2.x,其中的机制在 Python 3 中已经改变了。
type(type) is type
在 Python 2.6 中现在也可用使用 class decorators 来实现许多以前可能需要用元类来实现
的东西。
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.
最后,还有一个极尽奇技淫巧的例子(稍为深入,但仍然不难消化),可以去看看 The Selfless Metaclass。它通过字节码和方法签名重写来避免显式地声明 self 。