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之入门(五)缩进和选择
May 27 Python
Python的Bottle框架中返回静态文件和JSON对象的方法
Apr 30 Python
Windows上配置Emacs来开发Python及用Python扩展Emacs
Nov 20 Python
深入理解python中函数传递参数是值传递还是引用传递
Nov 07 Python
Python模拟随机游走图形效果示例
Feb 06 Python
django框架之cookie/session的使用示例(小结)
Oct 15 Python
Python Image模块基本图像处理操作小结
Apr 13 Python
Python实现二叉树前序、中序、后序及层次遍历示例代码
May 18 Python
用python打开摄像头并把图像传回qq邮箱(Pyinstaller打包)
May 17 Python
利用python对excel中一列的时间数据更改格式操作
Jul 14 Python
pycharm不以pytest方式运行,想要切换回普通模式运行的操作
Sep 01 Python
Python中json.dumps()函数的使用解析
May 17 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
如何将一个表单同时提交到两个地方处理
2006/10/09 PHP
ThinkPHP2.0读取MSSQL提示Incorrect syntax near the keyword 'AS'的解决方法
2014/06/25 PHP
PHP 双链表(SplDoublyLinkedList)简介和使用实例
2015/05/12 PHP
PHP二分查找算法示例【递归与非递归方法】
2016/09/29 PHP
PHP asXML()函数讲解
2019/02/03 PHP
详解PHP的抽象类和抽象方法以及接口总结
2019/03/15 PHP
PHP+redis实现微博的推模型案例分析
2019/07/10 PHP
ASP.NET jQuery 实例6 (实现CheckBoxList成员全选或全取消)
2012/01/13 Javascript
Js控制滑轮左右滑动实例
2015/02/13 Javascript
JavaScript使表单中的内容显示在屏幕上的方法
2015/06/29 Javascript
Node.js巧妙实现Web应用代码热更新
2015/10/22 Javascript
jQuery Ajax 实现在html页面实时显示用户登录状态
2016/12/30 Javascript
原生JS和jQuery操作DOM对比总结
2017/01/19 Javascript
jQuery实现的弹幕效果完整实例
2017/09/06 jQuery
使用Vue实现图片上传的三种方式
2018/07/17 Javascript
js操作table中tr的顺序实现上移下移一行的效果
2018/11/22 Javascript
js防抖和节流的深入讲解
2018/12/06 Javascript
Jquery的autocomplete插件用法及参数讲解
2019/03/12 jQuery
微信小程序生成分享海报方法(附带二维码生成)
2019/03/29 Javascript
详解微信图片防盗链“此图片来自微信公众平台 未经允许不得引用”的解决方案
2019/04/04 Javascript
JavaScript 引用类型实例详解【数组、对象、严格模式等】
2020/05/13 Javascript
Python和Ruby中each循环引用变量问题(一个隐秘BUG?)
2014/06/04 Python
python爬虫的工作原理
2017/03/05 Python
Python中关键字global和nonlocal的区别详解
2018/09/03 Python
python定时检测无响应进程并重启的实例代码
2019/04/22 Python
如何更优雅地写python代码
2019/07/02 Python
使用pyecharts生成Echarts网页的实例
2019/08/12 Python
Python完全识别验证码自动登录实例详解
2019/11/24 Python
简单了解python调用其他脚本方法实例
2020/03/26 Python
如何用Anaconda搭建虚拟环境并创建Django项目
2020/08/02 Python
python操作ini类型配置文件的实例教程
2020/10/30 Python
Python存储读取HDF5文件代码解析
2020/11/25 Python
美国基督教约会网站:ChristianCafe.com
2020/02/04 全球购物
舞蹈毕业生的自我评价
2014/03/05 职场文书
民族精神月活动总结
2014/08/28 职场文书
2015年党员承诺书
2015/01/21 职场文书