Python中装饰器兼容加括号和不加括号的写法详解


Posted in Python onJuly 05, 2017

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

@login_required
def my_view(request):
 ...
 return render(...)

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

@login_required(login_url='/accounts/login/')
def my_view(request):
 ...
 return render(...)

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

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

def doublewrap(f):
 '''
 a decorator decorator, allowing the decorator to be used as:
 @decorator(with, arguments, and=kwargs)
 or
 @decorator
 '''
 @wraps(f)
 def new_dec(*args, **kwargs):
  if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
   # actual decorated function
   return f(args[0])
  else:
   # decorator arguments
   return lambda realf: f(realf, *args, **kwargs)
 
 return new_dec

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

def test_doublewrap():
 from util import doublewrap
 from functools import wraps 
 
 @doublewrap
 def mult(f, factor=2):
  '''multiply a function's return value'''
  @wraps(f)
  def wrap(*args, **kwargs):
   return factor*f(*args,**kwargs)
  return wrap
 
 # try normal
 @mult
 def f(x, y):
  return x + y
 
 # try args
 @mult(3)
 def f2(x, y):
  return x*y
 
 # try kwargs
 @mult(factor=5)
 def f3(x, y):
  return x - y
 
 assert f(2,3) == 10
 assert f2(2,5) == 30
 assert f3(8,1) == 5*7

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

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

new_func = decorator(func)

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

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

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

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

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

参考资料

How to create a Python decorator that can be used either with or without parameters?

Python 相关文章推荐
python实现的登陆Discuz!论坛通用代码分享
Jul 11 Python
举例详解Python中的split()函数的使用方法
Apr 07 Python
Python输出9*9乘法表的方法
May 25 Python
Python实现周期性抓取网页内容的方法
Nov 04 Python
tensorflow: 查看 tensor详细数值方法
Jun 13 Python
在PyCharm下打包*.py程序成.exe的方法
Nov 29 Python
深入了解python中元类的相关知识
Aug 29 Python
Python 实现数组相减示例
Dec 27 Python
python集成开发环境配置(pycharm)
Feb 14 Python
使用Matplotlib绘制不同颜色的带箭头的线实例
Apr 17 Python
Pytorch中TensorBoard及torchsummary的使用详解
May 12 Python
如何用python清洗文件中的数据
Jun 18 Python
利用python模拟sql语句对员工表格进行增删改查
Jul 05 #Python
利用python实现简单的循环购物车功能示例代码
Jul 05 #Python
用python做一个搜索引擎(Pylucene)的实例代码
Jul 05 #Python
Python对象类型及其运算方法(详解)
Jul 05 #Python
python数据预处理之将类别数据转换为数值的方法
Jul 05 #Python
利用Python3分析sitemap.xml并抓取导出全站链接详解
Jul 04 #Python
在django中使用自定义标签实现分页功能
Jul 04 #Python
You might like
Amazon Prime Video平台《无限住人 -IMMORTAL-》2020年开始TV放送!
2020/03/06 日漫
让PHP支持页面回退的两种方法[转]
2007/02/14 PHP
第一个JavaScript入门基础 document.write输出
2010/02/22 Javascript
jquery将一个表单序列化为一个对象的方法
2013/12/02 Javascript
javascript算法题:求任意一个1-9位不重复的N位数在该组合中的大小排列序号
2015/04/01 Javascript
浅谈JavaScript字符串与数组
2015/06/03 Javascript
JS+CSS实现的竖向简洁折叠菜单效果代码
2015/10/22 Javascript
浅谈javascript基础之客户端事件驱动
2016/06/10 Javascript
js HTML5 Canvas绘制转盘抽奖
2020/09/13 Javascript
关于JavaScript和jQuery的类型判断详解
2016/10/08 Javascript
使用BootStrap实现悬浮窗口的效果
2016/12/13 Javascript
jQuery插件zTree实现清空选中第一个节点所有子节点的方法
2017/03/08 Javascript
原生JS+Canvas实现五子棋游戏
2020/05/28 Javascript
Angular2里获取(input file)上传文件的内容的方法
2017/09/05 Javascript
安装vue-cli报错 -4058 的解决方法
2017/10/19 Javascript
微信小程序实现image组件图片自适应宽度比例显示的方法
2018/01/16 Javascript
vue使用vue-i18n实现国际化的实现代码
2018/04/08 Javascript
JS中min函数实例讲解
2019/02/18 Javascript
vue 获取到数据但却渲染不到页面上的解决方法
2020/11/19 Vue.js
[01:31](回顾)杀出重围,决战TI之巅
2014/07/01 DOTA
[31:47]夜魇凡尔赛茶话会 第三期01:选手知多少
2021/03/11 DOTA
Python装饰器基础详解
2016/03/09 Python
Python3 模块、包调用&路径详解
2017/10/25 Python
python的staticmethod与classmethod实现实例代码
2018/02/11 Python
Python SQLite3简介
2018/02/22 Python
python实现静态web服务器
2019/09/03 Python
Python实现树莓派摄像头持续录像并传送到主机的步骤
2020/11/30 Python
网易微博Web App用HTML5开发的过程介绍
2012/06/13 HTML / CSS
凯伦·米莲女装网上商店:Karen Millen
2017/11/07 全球购物
Daisy London官网:英国最大的首饰集团IBB旗下
2019/02/28 全球购物
实习生个人的自我评价
2013/12/08 职场文书
心碎乌托邦的创业计划书范文
2013/12/26 职场文书
企业员工培训感言
2014/02/26 职场文书
革命英雄事迹演讲稿
2014/09/13 职场文书
生产操作工岗位职责
2014/09/16 职场文书
喜迎建国70周年:有关爱国的名言名句
2019/09/24 职场文书