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 相关文章推荐
wxpython学习笔记(推荐查看)
Jun 09 Python
进一步探究Python的装饰器的运用
May 05 Python
python实现将英文单词表示的数字转换成阿拉伯数字的方法
Jul 02 Python
python字符串连接方法分析
Apr 12 Python
Python学习教程之常用的内置函数大全
Jul 14 Python
Python实现的破解字符串找茬游戏算法示例
Sep 25 Python
unittest+coverage单元测试代码覆盖操作实例详解
Apr 04 Python
pd.DataFrame统计各列数值多少的实例
Dec 05 Python
pycharm下配置pyqt5的教程(anaconda虚拟环境下+tensorflow)
Mar 25 Python
Python3.7 读取音频根据文件名生成脚本的代码
Apr 07 Python
python利用platform模块获取系统信息
Oct 09 Python
利用python 下载bilibili视频
Nov 13 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
全国FM电台频率大全 - 25 云南省
2020/03/11 无线电
用PHP生成自己的LOG文件
2006/10/09 PHP
PHP session_start()问题解疑(详细介绍)
2013/07/05 PHP
体育彩票排列三组选三算法分享
2014/03/07 PHP
PHP递归调用数组值并用其执行指定函数的方法
2015/04/01 PHP
WordPress中用于更新伪静态规则的PHP代码实例讲解
2015/12/18 PHP
[原创]smarty简单模板变量输出方法
2016/07/09 PHP
php正则判断是否为合法身份证号的方法
2017/03/16 PHP
php面向对象程序设计中self与static的区别分析
2019/05/21 PHP
jQuery EasyUI常用数据验证汇总
2016/09/18 Javascript
微信小程序 欢迎页面的制作(源码下载)
2017/01/09 Javascript
React Native中的RefreshContorl下拉刷新使用
2017/10/09 Javascript
解决vue2.0 element-ui中el-upload的before-upload方法返回false时submit()不生效问题
2018/08/24 Javascript
JavaScript Canvas编写炫彩的网页时钟
2019/10/16 Javascript
vue+vant-UI框架实现购物车的复选框全选和反选功能
2019/11/05 Javascript
npx create-react-app xxx创建项目报错的解决办法
2020/02/17 Javascript
vue实现防抖的实例代码
2021/01/11 Vue.js
利用python实现简单的邮件发送客户端示例
2017/12/23 Python
Python使用matplotlib绘制多个图形单独显示的方法示例
2018/03/14 Python
python之cv2与图像的载入、显示和保存实例
2018/12/05 Python
django云端留言板实例详解
2019/07/22 Python
python模拟鼠标点击和键盘输入的操作
2019/08/04 Python
Python自动化测试笔试面试题精选
2020/03/12 Python
python 19个值得学习的编程技巧
2020/08/15 Python
详解HTML5 canvas绘图基本使用方法
2018/01/29 HTML / CSS
顶级宝石首饰网络零售商:Angara
2016/10/25 全球购物
北美个性化礼品商店:Things Remembered
2018/06/12 全球购物
测绘工程本科生求职信
2013/10/10 职场文书
口腔医学技术应届生求职信
2013/11/09 职场文书
2014酒店客房部工作总结
2014/12/16 职场文书
订货会邀请函
2015/01/31 职场文书
小学三八妇女节活动总结
2015/02/06 职场文书
上下班时间调整通知
2015/04/23 职场文书
SqlServer: 如何更改表的文件组?(进而改变存储位置)
2021/04/05 SQL Server
React Native项目框架搭建的一些心得体会
2021/05/28 Javascript
使用 CSS 构建强大且酷炫的粒子动画效果
2022/08/14 HTML / CSS