注册回调函数应该是开发中很常见的一种行为。这在 Python 中通常通过装饰器来实现,看起来比较漂亮:
但是这种用法常常带来一种隐藏的“惊讶”,比如说:
这个 @permission 根本不会生效,因为在它装饰 admin_menu 之前,admin_menu 就已经被注册到 app 里了。最终我们执行的是未经过装饰的函数。
类似的陷阱还有可能出现在“猴子补丁”使用的场景。比如下面这段代码将一系列 filter 注册到一个对象中:
看起来似乎很漂亮,但是如果我在写单元测试的时候,希望用一个 Stub 来取代 datetime_filter ,就会小小惊讶一下:
说到底还是用装饰器注册函数,不是 Lazy Binding 的。
这是我个人感觉 Python 的装饰器使用中比较违反直觉的一个地方。虽然装饰器这种看上去很像“声明”的语法很酷,却也很 implicit 。当然因为装饰器的这种用法实在太流行,很多 Python 开发者已经具备了绕过这个陷阱的技能了(Orz,小白和非小白的区别之一)。但是我还是觉得这种东西需要开发者用经验去绕过的,并不符合我比较认同的最小惊讶原则。
所以…… 我可能还是更喜欢用 Qualified Name 来引用要注册的函数,以实现真正的 Lazy Binding: