使用Django的时候,我发现一个很神奇的装饰器:@login_required, 这是控制一个view的权限的,比如一个视图必须登录才可以访问,可以这样用:

1

2

3

4

@login_required

defmy_view(request):

...

returnrender(...)

同时,如果要达到这样一种效果:如果用户没有登录,那么就把用户重定向到登录界面,可以这样用:

1

2

3

4

@login_required(login_url='/accounts/login/')

defmy_view(request):

...

returnrender(...)

所以这个装饰器可以带括号写,又可以不带括号写。很神奇有没有。正常的接收参数的装饰器,就算没参数也应该写成@login_required的

好奇去查了一下,在stackoverflow找到一种实现,挺有意思的。先晒出答案:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

defdoublewrap(f):

'''

a decorator decorator, allowing the decorator to be used as:

@decorator(with, arguments, and=kwargs)

or

@decorator

'''

@wraps(f)

defnew_dec(*args,**kwargs):

iflen(args)==1andlen(kwargs)==0andcallable(args[0]):

# actual decorated function

returnf(args[0])

else:

# decorator arguments

returnlambdarealf:f(realf,*args,**kwargs)

returnnew_dec

使用起来很简单,只要给装饰器用@doublewrap装饰一下,这个装饰器就支持写括号和不写括号两种写法了。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

deftest_doublewrap():

fromutilimportdoublewrap

fromfunctoolsimportwraps

@doublewrap

defmult(f,factor=2):

'''multiply a function's return value'''

@wraps(f)

defwrap(*args,**kwargs):

returnfactor*f(*args,**kwargs)

returnwrap

# try normal

@mult

deff(x,y):

returnx+y

# try args

@mult(3)

deff2(x,y):

returnx*y

# try kwargs

@mult(factor=5)

deff3(x,y):

returnx-y

assertf(2,3)==10

assertf2(2,5)==30

assertf3(8,1)==5*7

原理也不难,只有短短不到10行代码。

装饰器我们都知道,是用来处理一个函数,返回一个新的函数的(如果你不理解装饰器,可以看一下这个经典的解释)。

1

new_func=decorator(func)

我们使用的,就是被装饰器装饰的新函数了。装饰器只是一个语法糖,其实它也是一个函数,给它传入一个函数作为参数,就返回一个新的函数。那么既然装饰器也是一个函数,我们就可以用装饰器装饰这个函数。也就是,“装饰器的装饰器”。

装饰器第一个参数肯定是原函数,如果装饰器可以接收参数的话,要么第一个参数是原函数,后面跟别的参数;要么就只有原函数一个参数。所以,我们这个“装饰器的装饰器”做的事情就是,判断装饰器接收的参数,如果只有一个并且第一个参数是可调用的(callable),那么这就是一个无参数的装饰器(不需要加括号)。如果还有别的参数,就返回一个生成装饰器的函数(decorator_maker)。

装饰器是一个函数。装饰器被装饰过之后,这个装饰器运行之前就会先运行装饰器的装饰器的代码,也就是我们的doublewrapp。然后返回值可能是一个装饰器,也可能是一个装饰器的maker(有参数的装饰器),然后装饰器再执行,装饰原函数。

这里有点绕,因为本来装饰器里面一般就会有三四层函数了,(maker, decorator, wrapper, realfunc),再加上一个装饰器的装饰器,会有点理解困难。如果理解不了,最好不要对着网上的博文(包括本文)企图格物致知了,多去看看代码,多写一写。

2017年12月31日更新:《Python3 Cookbook》中提供了另一种实现方法,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

fromfunctoolsimportwraps,partial

importlogging

deflogged(func=None,*,level=logging.DEBUG,name=None,message=None):

iffuncisNone:

returnpartial(logged,level=level,name=name,message=message)

logname=nameifnameelsefunc.__module__

log=logging.getLogger(logname)

logmsg=messageifmessageelsefunc.__name__

@wraps(func)

defwrapper(*args,**kwargs):

log.log(level,logmsg)

returnfunc(*args,**kwargs)

returnwrapper

# Example use

@logged

defadd(x,y):

returnx+y

@logged(level=logging.CRITICAL,name='example')

defspam():

print('Spam!')

这种原理也比较好理解,看例子1,我们知道这等价于logged(add),第一个参数是函数,所以直接返回。看例子2,等价于logged(level=logging.CRITICAL, name='example')(spam),logged的第一个参数func是None(如果是装饰器语法,第一个参数会默认传入函数,而这里直接是函数调用,并非装饰器。所以会返回一个partial函数,这个partial函数是真正的装饰器,然后再走后面装饰器的路子。其实,这种方法也是利用了“判断传入装饰器的参数”。

参考资料

python if后面要不要加括号_Python装饰器兼容加括号与不加括号的写法相关推荐

  1. python程序会监控错误的语句_python装饰器实现对异常代码出现进行自动监控

    异常,不应该存在,但是我们有时候会遇到这样的情况,比如我们监控服务器的时候,每一秒去采集一次信息,那么有一秒没有采集到我们想要的信息,但是下一秒采集到了, 而后每次的采集都能采集到,就那么一次采集不到 ...

  2. python 装饰器实现事件绑定_Python装饰器是怎么实现的?

    Python中的装饰器是通过利用了函数特性的闭包实现的,所以在讲装饰器之前,我们需要先了解函数特性,以及闭包是怎么利用了函数特性的 ① 函数特性 python中的函数特性总的来说有以下四点: 1. 函 ...

  3. python实现装饰器_Python装饰器是怎么实现的?

    Python中的装饰器是通过利用了函数特性的闭包实现的,所以在讲装饰器之前,我们需要先了解函数特性,以及闭包是怎么利用了函数特性的 ① 函数特性 python中的函数特性总的来说有以下四点: 1. 函 ...

  4. python装饰器与闭包_Python 装饰器和闭包

    Python 装饰器和闭包 装饰器是 Python 中常见的语法糖,这篇文章讲了闭包和装饰器的原理,并且分析了函数中变量的作用域,以及尝试总结了常见的坑. 装饰器基础 首先来看看装饰器的定义:装饰器本 ...

  5. python中的装饰器怎么运行_Python 装饰器入门(上)

    翻译前想说的话: 这是一篇介绍python装饰器的文章,对比之前看到的类似介绍装饰器的文章,个人认为无人可出其右,文章由浅到深,由函数介绍到装饰器的高级应用,每个介绍必有例子说明.文章太长,看完原文后 ...

  6. python 开闭原则_Python 装饰器@,对函数进行功能扩展操作示例【开闭原则】

    本文实例讲述了Python 装饰器@,对函数进行功能扩展操作.分享给大家供大家参考,具体如下: 装饰器可以对原函数进行功能扩展,但还不需要修改原函数的内容(开闭原则),也不需要修改原函数的调用. de ...

  7. python 装饰器实现_Python装饰器系列01 - 如何正确地实现装饰器

    虽然人们能利用函数闭包(function clouser)写出简单的装饰器,但其可用范围常受限制.多数实现装饰器的基本方式会破坏与内省(Introspection)的关联性. 可大多数人会说:who ...

  8. python3装饰器详解_Python装饰器详解

    按照 Python 的编程原则,当一个函数被定义后,如要修改或扩展其功能应尽量避免直接修改函数定义的代码段,否则该函数在其他地方被调用时将无法正常运行.因此,当需要修改或扩展已被定义的函数的功能而不希 ...

  9. python 递归函数_让你Python到很爽的加速递归函数的装饰器

    Python技巧--好用的一个装饰器 今天我们会讲到一个装饰器. 注记:链接"装饰器"指向廖雪峰老师的Python3教程中的装饰器教程.可以在这里快速了解什么是装饰器. `@fun ...

最新文章

  1. Java-COW在Java中的应用
  2. angularjs 读取mysql_如何使用AngularJS PHP从MySQL获取数据
  3. 通过java类的反射机制获取类的属性类型
  4. Linux把文件移动到容器外,Docker容器与主机之间拷贝文件的方法
  5. leetcode237 删除链表中的节点(你意想不到的做法,注意细节)
  6. linux的任务计划6,Linux计划任务
  7. java输出object object_js控制台显示[object Object]问题
  8. centos服务端ftp的搭建_centos下搭建ftp服务器
  9. [DFS|回溯法] leetcode 17 电话号码的字母组合
  10. inout口简单说明
  11. 在mysql的操作界面中,如何清屏幕
  12. Oracle 备份shell,oracle数据库shell备份脚本
  13. 4.Kong入门与实战 基于Nginx和OpenResty的云原生微服务网关 --- Kong 的基本功能
  14. 网络安全从小白到专家
  15. matlab 添加断点,matlab设置断点
  16. 统信操作系统 摄像头驱动程序
  17. 蒲丰投针结果_Scratch3.0模拟布丰投针,求π的近似值#寻找真知派#
  18. C/C++蓝桥杯三升序列
  19. 快速学习-cmd命令大全
  20. 如何通过Apple ID找回弄丢的设备

热门文章

  1. 拯救颓废假期!快来和我们一起刷论文写笔记
  2. HDU1250 Hat's Fibonacci 大数斐波那契数列
  3. java中文 x_java环境url中文参数乱码处理
  4. oracle监听静态注册,oracle监听器动态注册于静态注册的区别
  5. 服务器装系统03系统,day03服务器操作系统安装
  6. 《IBM-PC汇编语言程序设计》(第2版)【沈美明 温冬婵】——第四章——自编解析与答案
  7. [AHOI2005]COMMON 约数研究
  8. Game with modulo
  9. Ajax服务器响应简单实例
  10. Android复习15【动画:创建资源文件夹、创建动画资源文件、组合动画、属性动画、材料设计新特性】