浅析 Python 的 metaclass(2)

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...

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwdxxd.html