1004
Python
Python 元类初探
作者:西北那个峰
先以一个大牛的一段关于Python Metapgramming的着名的话来做开头:
Metaclasses are deeper magic than 99% of users should ever worry about. 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
翻译一下:
Metaclasses是99%的用户都无需费神的黑科技。如果你还在纠结你是不是需要它的话,答案是NO (真正需要的人根本不需要解释) - Tim Peters
这是什么鬼话?道可道,非常道吗?
好,装B已毕。这确实是一个冷僻的,不常用的话题。一篇短文肯定讲不完。 所以叫做初探。
英文meta这个词其实是从希腊语里面借来的。wikipedia上的解释是:
indicate a concept which is an abstraction behind another concept, used to complete or add to the latter
不看还好,其实看了更晕。好在后面的解释有一句"更高一层的抽象",可以帮助理解。 其实我们可以这样理解。meta的意思就是"关于什么的什么":比如metadata可以理解为"关于数据的数据",metaprogramming可以理解为"关于编程的编程"。这就和"更高一层的抽象" 比较契合了。同时又隐隐和编程中的另一个永恒主题-recursion联系在了一起。
另外,meta这个词天朝这边翻译成"元",海峡对岸翻译成"后设"。其实我都不大理解从何而来。
聚焦到我们今天的主题,metaprogramming就是编写用来生成代码的代码。
假设我们写了一个NB的函数,用来计算一个任意复杂的算数表达式的值:
像1+2, 3*6+10, 什么的都可以交给它去计算。这样的函数的算法不是我们的主题,所以我们请出python自带的大招 ,一行就可以搞定了:
因为输入的可能性是无限的,所以我们肯定要好好测试一下这个函数了。假定我们想了 上百个test case。又假定我们是用 这个module来做测试的。这样的测试程序一般会长成这样:
所以我们的目的就是用metaprogramming的方式来自动产生类似上面的测试类。
先上程序后解释:
NB的 函数我们解释过了。main这段也比较简单:我们用声明的方式定义了一组测试,然后通过 来执行。
有点复杂的是 。我们先来看看最内层的 这个方法。逻辑上,它就是把输入的测试用例分成两份,一份是 的输入,一份是我们期待的结果;然后调用 , 接着用 来测试。
但是这个 有点奇怪 - 这里没有类,哪里来的 ? 其实 确实是一个类的方法,只不过这个类是我们通过代码动态生成的。也就是下面这一行:
这里的 就是通常我们用来检查某个变量的类型的那个函数。只不过它还有另外一种不大为人知的形式:
这第二种形式,就会产生一个新的类型。以我们的程序为例,就是以 为baseclass, 产生了一个名为 的新类型,改类型的实现由 给出,而 就包含了通过closure返回的 这个方法。只不过在这个新类里面,它的名字叫做 。
最后,我们把这个新产生的类加入到当前全局符号表里面,也就相当于上面给出的unittest的例子。
所以,总结一下。当我们运行这个脚本的时候,这段比较短的代码会针对每一个测试的表达式产生一个新的测试类,并动态生成测试的方法加载到该类里面。 从 中找到这些类并一一执行测试。
上面的例子中,其实一行一行手打 也没什么大不了的。但是当你要表达的逻辑比较复杂的时候,metaprogramming的强大就体现出来了。
那么,看完这篇文章,我们也成为Tim所说的1%的程序猿了!其实,也许他的意思是,99%的编程工作都用不到这样技巧。在一些特殊的场合,比如编写某种框架的时候,metaprogramming会做到事半功倍。祝你在实践中碰到这样的机会。
题图:pexels,CC0 授权。
最后更新:2017-10-25 11:57:49