Python装饰器应该多久使用一次?
我最近开始尝试使用Python的装饰器(还有高阶函数),因为我觉得它们可以让我的Django单元测试写得更简洁。比如,我本来需要写:
def visit1():
login()
do_stuff()
logout()
我现在可以这样写:
@handle_login
def visit1():
do_stuff()
不过,经过一段时间的尝试,我发现装饰器并没有我想象中那么简单。首先,我看到的不同例子中有各种各样的装饰器语法,让我感到困惑,直到我明白装饰器在< a href="http://www.artima.com/weblogs/viewpost.jsp?thread=240845" rel="nofollow noreferrer">接受参数时的表现是非常不同的。接着,我尝试给一个方法加装饰器,结果发现不管用,因为我首先得把我的装饰器变成一个描述符,这需要添加一个__get__
方法。在这个过程中,我几次感到困惑,而且发现调试这些“装饰过的”代码比正常的Python代码要复杂得多。现在我在重新考虑我是否真的需要在代码中使用装饰器,因为我最开始的动机只是想省点打字,而不是因为有什么东西真的需要高阶函数。
所以我的问题是:装饰器应该多用还是少用?有时候不使用它们反而更符合Python的风格吗?
4 个回答
装饰器是一种将常见的方面从你的代码中提取出来的方法。
支持面向方面编程(AOP)的人会告诉你,有很多常见的方面,所以AOP是非常重要的。实际上,你可以在这里看到关于这个话题的一个有趣争论:
AOP有一些常见的使用场景。你可以在这里了解一些:
有一些跨越多个功能的关注点,装饰器在这些方面非常有用。
访问控制(“安全”)身份验证、授权、权限、所有权
日志记录(包括调试辅助工具和审计)
缓存(通常是对记忆化的一种实现)
一些错误处理可能是一个常见的方面,因此适合用装饰器来实现。
实际上,真正跨越多个功能并值得使用AOP装饰器的设计模式非常少。
Alex已经很好地回答了你的问题,我想补充一点,就是使用装饰器,这样可以让你的代码更容易理解。(有时候,即使你只用一次,效果也很好。)
举个例子,最开始我写Django的视图时,根本没考虑授权的问题。等我写完后,我能看到哪些视图需要授权用户,然后就给它们加上一个@login_required。
这样,后面的人一眼就能看出哪些视图是需要授权的。
当然,这样做也能让代码更简洁,不用到处重复。
比如,如果用户没有登录:
if not request.user.is_authenticated():
就可以返回一个重定向的响应。
装饰器在合适的地方是很不错的,绝对不是要避免的东西;当用得对的时候,它们是很有用的。你问的问题其实就是“那么,什么时候用装饰器比较合适呢?”
一个好的例子就是在某些类的部分方法前后加上一些额外的代码。如果是所有的方法都需要加,那用一个类装饰器来包裹所有的方法会比到处重复写@thisonetoo
要好得多。如果这种情况很少发生,那就没必要为了这点小事去重构代码(用装饰器或者其他方式)。在这两者之间,有很多情况是装饰器非常合适的。
这其实归结为编程中的一个黄金法则——DRY,意思是“不要重复自己”。当你发现代码中有重复的部分时,就应该把这些重复的部分提取出来,而装饰器就是一个很好的工具,虽然它不是唯一的选择(还有辅助方法和函数、自定义元类、生成器和其他迭代器、上下文管理器……我们在过去几年里为Python增加的许多功能都可以看作是帮助实现DRY的工具,让提取重复代码变得更简单、更顺畅!)。
如果没有重复的代码,那就没有必要去重构,因此(特别是)也就不需要装饰器——在这种情况下,YAGNI(你不会需要它)可以比DRY更重要;-)