Python metaclass 的使用
之前有个需求,需要去监控某些类的所有的函数调用的耗时,当时团队里面最开始的方案是通过继承某个基类来实现
1 |
|
这是团队里面一个人写的初始版本,十分的清晰易懂,通过改写 ___getattribute__
方法,只对方法调用进行监控,对异常和返回值都原样处理。唯一的不同就是调用方法时使用 Timer 进行耗时计算。
这样子的写法的好处是,只要是基类继承了该监控类,后续的子类也会有相同的效果,但这里也衍生出另外的问题
- 因为
__getattribute__
是实例方法,对类中的 staticmethod 和 classmethod 方法没有效果 - 每次调用都会重新把方法重新绑定到 self 中
后续的讨论中主要是担心第二点会带来性能损耗。哪怕你对某些函数设置了标记位从而不去进行监控,因为你重写了这个 __getattribute__
方法,在实际使用上还是得去重新绑定到 self 上去。
metaclass
后面有人提出了元类的改进方案,具体代码如下
1 |
|
这里涉及的改进是
- 通过装饰器去设置标记位
TIMED_METHOD_FLAG
,标记某些方法不进行收集,默认情况下除去一些 magic method 都进行收集 - 可以感知到 staticmethod 和 classmethod,但需要进一步的判断方法
- 只有需要收集的方法会有额外的操作,别的方法没有额外的操作
但使用元类又有一个比较纠结的问题,在上述的例子中,C 使用了元类,D 继承 C,如果通过 D 的实例调用方法 hah
则实际上记录到的是 C.hah
。
这里,我们希望知道的是具体的类对象的调用的时延,使用元类(之前的继承的方法没有这个问题)的话,可能会有一些这样造成困惑的数据。
conclusion
这是我第一次涉及到 Python 的元类相关的具体应用,最开始理解起来觉得比较绕,但实际上你记住 Python 里面一切皆对象就很容易理解了,类其实也是一种对象,我们可以通过元类去产生具体的类,由具体的类再产生对象
1 |
|
后续参考中的答案写得非常清晰,强烈推荐阅读。
references
Python metaclass 的使用
http://yoursite.com/2018/09/15/use-python-metaclass/