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

时间:2015-06-13 16:27:52

标签: python decorator python-decorators

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

例如,this StackOverflow question and its various answers对如何执行此操作提供了很多见解。然而,对于看起来在概念上简单的东西,它们都显得相当惊人地复杂(要么是额外的条件逻辑或更深的函数嵌套)。我最关心的是,在给出的所有示例中,> 50%的代码与该特定装饰器的行为无关,并且是使用该模式编写的所有装饰器之间的共享样板。

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

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

提前致谢!

2 个答案:

答案 0 :(得分:1)

我认为functools.wraps确实代表了样板抽象。我不认为这很麻烦。我通常在没有它的情况下对装饰器进行编码,测试它,然后在functools.wraps编织后检查测试是否仍然有效。增加15分钟的上衣。

那就是说,我不认为为每个装饰者使用functools包装是必要的。对于纯粹内部的功能,以及那些不需要文档字符串的自我解释的功能,我经常不会因为坚持他们的装饰完整包装而烦恼。

最后,我非常强烈地相信,在@foo(bar)是一个选项的情况下,您可以创建foo作为"装饰工厂"。 (即,返回装饰器的函数)。然后,您使用@foo()@foo(bar)。我认为这是处理参数化超出单个参数的装饰器的正确方法。在我看来,装饰器总是函数,它接受一个参数并返回该参数的修改和/或包装。

答案 1 :(得分:1)

PyPI上的decoratorwraptdecorators软件包提取了functools.wraps提供的一些样板代码。

如果您要同时使用@foo@foo(args),则必然会在代码中产生额外的复杂性,因为foo(func)foo(args)(func)是完全不同的方式来包装功能。我同意Pete的观点,@foo()在这种情况下更清楚。