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()
在那种情况下更清楚。
我在 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()
在那种情况下更清楚。