Python 装饰器模式:减少涉及内部函数和 functools.wraps 的代码重复

Python decorator pattern: reducing code duplication involving inner functions and functools.wraps

我在 Whosebug 和其他地方看到了很多关于如何编写 Python 装饰器的文档。他们通常建议使用 functools.wraps 和(可能是多个)内部函数。特别复杂的是,如果我想要一个可以带括号或不带括号调用的装饰器,即 @foo@foo(bar).

例如,this Whosebug question and its various answers 给出了很多关于如何做到这一点的见解。然而,对于概念上看似简单的东西,它们都显得相当复杂(额外的条件逻辑或更深层次的函数嵌套)。我最担心的是,在给出的所有示例中,>50% 的代码与特定装饰器的行为无关,并且在使用该模式编写的所有装饰器之间共享样板。

我看到的真实示例是 the Fabric project's decorators.py and some of the Django project's various decorator.py instances。有很多与实际意图无关的样板代码对我来说似乎有点奇怪。

出于代码可维护性的原因,我理解为什么要使用 functools.wraps,但这似乎过于复杂。有什么方法可以 DRY and/or 封装这段代码吗?从用户代码的角度来看,我真正关心的唯一部分似乎是生成的内部函数的实际主体。我怎么能把剩下的样板写一次并永远重复使用呢?

提前致谢!

我认为 functools.wraps 确实代表了样板抽象。我觉得没那么麻烦。我通常在没有它的情况下编写一个装饰器,对其进行测试,然后在 functools.wraps 中编织后检查测试是否仍然有效。最多增加 15 分钟。

也就是说,我认为没有必要为每个装饰器都使用 functools 包装。对于纯粹内部的函数,以及不需要文档字符串的不言自明的函数,我通常不会费心坚持让它们的装饰执行完整的包装。

最后,我非常坚信,在 @foo(bar) 是一个选项的情况下,您创建 foo 成为 "decorator factory"。 (即 returns 装饰器的函数)。然后,您将使用 @foo()@foo(bar)。我认为这是处理参数化超出其单个参数的装饰器的正确方法。在我看来,装饰器始终是接受一个参数的函数,并且 returns 修改 and/or 该参数的包装。

PyPI 上的 decorator, wrapt, and decorators 包抽象出一些超出 functools.wraps 提供的样板代码。

如果您想同时使用 @foo@foo(args),您的代码必然会更加复杂,因为 foo(func)foo(args)(func) 是完全不同的方式包装一个函数。我同意皮特的观点,@foo() 在那种情况下更清楚。