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中使用sys模板和logging模块获取行号和函数名的方法
Apr 15 Python
python实现从一组颜色中找出与给定颜色最接近颜色的方法
Mar 19 Python
在PYQT5中QscrollArea(滚动条)的使用方法
Jun 14 Python
python+openCV利用摄像头实现人员活动检测
Jun 22 Python
python numpy 常用随机数的产生方法的实现
Aug 21 Python
windows下Python安装、使用教程和Notepad++的使用教程
Oct 06 Python
Django框架安装方法图文详解
Nov 04 Python
python清空命令行方式
Jan 13 Python
jupyter 实现notebook中显示完整的行和列
Apr 09 Python
不到20行实现Python代码即可制作精美证件照
Apr 24 Python
在Python中字典按值排序的实现方法
Nov 12 Python
python爬虫实现爬取同一个网站的多页数据的实例讲解
Jan 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
dedecms中常见问题修改方法总结
2007/03/21 PHP
php查找字符串出现次数的方法
2014/12/01 PHP
php+ajax注册实时验证功能
2016/07/20 PHP
PHP开发APP端微信支付功能
2017/02/17 PHP
input 高级限制级用法
2009/03/26 Javascript
js实现网页自动刷新可制作节日倒计时效果
2014/05/27 Javascript
JavaScript模拟可展开、拖动与关闭的聊天窗口实例
2015/05/12 Javascript
JS访问SWF的函数用法实例
2015/07/01 Javascript
基于jquery实现图片相关操作(重绘、获取尺寸、调整大小、缩放)
2015/12/25 Javascript
js实现的页面加载完毕之前loading提示效果完整示例【附demo源码下载】
2016/08/02 Javascript
JavaScript每天必学之数组和对象部分
2016/09/17 Javascript
Angular 中 select指令用法详解
2016/09/29 Javascript
用原生js做单页应用
2017/01/17 Javascript
基于Bootstrap漂亮简洁的CSS3价格表(附源码下载)
2017/02/28 Javascript
使用vs code开发Nodejs程序的使用方法
2017/09/21 NodeJs
在React项目中使用Eslint代码检查工具及常见问题
2018/10/10 Javascript
判断文字超过2行添加展开按钮,未超过则不显示,溢出部分显示省略号
2019/04/28 Javascript
vue动态注册组件实例代码详解
2019/05/30 Javascript
JavaScript使用canvas绘制随机验证码
2020/02/17 Javascript
微信小程序实现搜索功能
2020/03/10 Javascript
VSCode写vue项目一键生成.vue模版,修改定义其他模板的方法
2020/04/17 Javascript
jQuery 动画与停止动画效果实例详解
2020/05/19 jQuery
element-ui封装一个Table模板组件的示例
2021/01/04 Javascript
Django高级编程之自定义Field实现多语言
2019/07/02 Python
pybind11在Windows下的使用教程
2019/07/04 Python
python如何给字典的键对应的值为字典项的字典赋值
2019/07/05 Python
python实现多进程按序号批量修改文件名的方法示例
2019/12/30 Python
keras 解决加载lstm+crf模型出错的问题
2020/06/10 Python
Python实现迪杰斯特拉算法过程解析
2020/09/18 Python
10分钟理解CSS3 FlexBox弹性布局
2018/12/20 HTML / CSS
什么是Rollback Segment
2013/04/22 面试题
新领导上任欢迎词
2014/01/13 职场文书
安全检查管理制度
2014/02/02 职场文书
浅谈@Value和@Bean的执行顺序问题
2021/06/16 Java/Android
Golang的继承模拟实例
2021/06/30 Golang
MySQL 字符集 character
2022/05/04 MySQL