1. metaclass的使用原则:
If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why). --Tim Peters
也就是说如果你不知道能用metaclass来干什么的话,你尽量不要用,因为通常metaclass的代码会增加代码的复杂度,降低代码的可读性。所以你必需权衡metaclass的利弊。
2. metaclass的优势在于它的动态性和可描述性(比如上面例子中的self.delegate.__getitem__(i)这样的代码,它可以用另外的函数代码生成,而无需每次手动编写), 它能把类的动态性扩展到极致。
六 补充
以下是同事们的很好的补充:
张同学:
1.metaclass属于元编程(metaprogramming)的范畴,所谓元编程就是让程序来写(generate/modify)程序,这通常依赖于语言及其运行时系统的动态特性(其实像C这样的语言也可以进行元编程)。正如楼主所说,元编程的一个用途就是“可以用另外的函数代码生成,而无需每次手动编写“,在Python中我们可以做得更多。
2.对于python而言,metaclass使程序员可以干涉class的创建过程,并可以在任何时候修改这样的class(包括修改metaclass),由于class的意义是为instance集合持有“方法”,所以修改了一个class就等于修改了所有这些instance的行为,这是很好的service。
3.注意metaclass的__new__和__init__的区别。
class DynamicMethod(type):
def __new__(cls, name, bases, dct): # cls=DynamicMethod
def __init__(cls, name, bases, dct): # cls=你创建的class对象
这意味着在__new__中我们通常只是修改dct,但是在__init__中,我们可以直接修改创建好的类,所以我认为这两个接口的主要区别有2点:1)调用时机不同(用处请发散思维);2)__init__比__new__更有用,我在实际项目中一般都是用__init__的。
4.在python中我们为什么要修改class?那当然是为了改变它的行为,或者为了创建出独一无二的类。实际中常常需要为class动态添加方法。比如一个数据库表A有字段name, address等,表B有name, phone等,你希望A的模型类有find_by_address、find_by_name_and_address等方法,希望B的模型类有find_by_name、find_by_phone等方法,但是又不想手写这些方法(其实不可能手写,因为这种组合太多了),这时你可以在A、B共同的metaclass中定义一个自动添加方法的子程序,当然你也可以重写__getattr__之类的接口来hook所有find_by_XXX函数调用,这就是时间换空间了,想象你要执行find_by_XXX一百万次。也可以比较一下在c++/Java中如何应对这种需求。
5.python的成功之处在于它无处不在的namespace(就是那个__dict__,其意义可以参考SICP第一章的environment模型,对计算理论感兴趣的也可以用lambda演算来解释),而且函数又是first class对象,又强化了interface的使用。我们知道了metaclass->class->instance的关系,又知道了对象的方法是放在类里的(请详细考察python查找一个方法的流程),那么用python实现各种设计模式就比较简单了。
6.metaclass不会使程序变晦涩,前提是了解了metaclass的固有存在,许多教程的问题就在于它没有告诉读者metaclass的存在,而是借助某些其他语言(比如c++)的类模型来讲解python。在ruby的类型系统中metaclass是无限的,metaclass也有自己的metaclass(你可以称之为metametaclass、metametametaclass等等),ruby善于实现DSL和语法分析器也部分得益于此。
岳同学:
不能说__init__比__new__更有用吧。我觉得要看场合。毕竟__new__能做到比__init__更多的事情。比如有时候想改生成的类型名字,或者改类型的父类。:)
不过的确大多数场合用__init__就够用了。+1
在__init__中控制类生成的过程有一点要注意:在__init__()的最后一个参数(attrs)中,对于类中定义的函数类型的属性,比如:
def abc(self):
pass
仍然具有以下的key->value形式:
"abc":<function object>
但是在生成的类中,"abc"对应的属性已经从一个function变成了一个unbind
method:
self.abc --> unbind method
不过实际使用中影响不大。
七 其他
1. 哪些项目有用metaclass:
据我所知就是django中的数据库部分的很多都使用metaclass来实现可描述性的
还有google app engine的代码里面也有使用
yaml中的序列化部分代码也有使用
more...