我是 sb!Python 都不会写!(大声)
背景
在一个奇怪的场景里触发了一个奇怪的问题。为了降低某个方法的 qps(?),写了一个摸鱼 decorator,使得该方法每 n 次调用只运行一次,其他时候返回上一次运行的结果。大概用法如下
1 |
|
这个 decorator 也不难写,只是需要维护一个计数器和上一次的结果,也就是说是一个有状态的 decorator(不保证这么说是否合理,可以意会一下)
1 | def do_every(time): |
因为其行为和摸鱼类似,因此命名为「摸鱼 decorator」
翻车
上面的代码看着写的很对(实际上也写的很对)
但实际上,遇到 class 后,可能产生和预期有出入的效果。
1 | import random |
在上面的代码中,我们定义了class A
,并给了一个成员方法func
。那么,在使用do_every
前,先思考一个问题
若存在两个
A
的实例,那他们应该各自摸鱼还是轮流摸鱼?
事实上,由于没有思考过这个问题,因此直接导致了翻车。
1 | a = A() |
摸鱼 decorator 最终起的作用是轮流摸鱼,参考如下输出
1 | a 0.06733737591002142 |
我们看到 4 轮中,a 干了两轮活,b 干了一轮活,加起来总共干了三次。实际上在使用上我们希望他们各自摸鱼,也就是 a 和 b 各需要干两轮活,总共加起来要干四次。资本家对此表达了强烈不满!
在实践中,我们发现 decorator 内部的变量,虽然在不同调用该 decorator 的函数间不共享(显然 decorator 的入参不同),但在 Python 中的成员方法,实际上应看做同一个函数(传入的 self 不同)
后续
再也不写有状态的 decorator 了!
由于没有想到好的修复方法,于是放弃了摸鱼(?),用别的办法绕过了这个问题。
有想到办法的牛逼网友,欢迎供稿。