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输出一个杨辉三角的例子
Jun 13 Python
使用Python中的线程进行网络编程的入门教程
Apr 15 Python
python基础之包的导入和__init__.py的介绍
Jan 08 Python
Python实现数据可视化看如何监控你的爬虫状态【推荐】
Aug 10 Python
Python函数和模块的使用总结
May 20 Python
python,Django实现的淘宝客登录功能示例
Jun 12 Python
TensorFlow基于MNIST数据集实现车牌识别(初步演示版)
Aug 05 Python
Python写出新冠状病毒确诊人数地图的方法
Feb 12 Python
Python decorator拦截器代码实例解析
Apr 04 Python
在服务器上安装python3.8.2环境的教程详解
Apr 26 Python
Python实现像awk一样分割字符串
Sep 15 Python
Python+Selenium随机生成手机验证码并检查页面上是否弹出重复手机号码提示框
Sep 21 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
PHP中仿制 ecshop验证码实例
2017/01/06 PHP
IE8 兼容性问题(属性名区分大小写)
2009/06/04 Javascript
javascript 异常处理使用总结
2009/06/21 Javascript
javascript通过className来获取元素的简单示例代码
2014/01/10 Javascript
JavaScript控制各种浏览器全屏模式的方法、属性和事件介绍
2014/04/03 Javascript
table insertRow、deleteRow定义和用法总结
2014/05/14 Javascript
jQuery实现视频作为全屏幕背景
2014/12/18 Javascript
JavaScript中数据结构与算法(二):队列
2015/06/19 Javascript
JS日期加减,日期运算代码
2015/11/05 Javascript
全面解析Angular中$Apply()及$Digest()的区别
2016/08/04 Javascript
浅谈jquery中next与siblings的区别
2016/10/27 Javascript
浅谈js对象属性 通过点(.) 和方括号([]) 的不同之处
2016/10/29 Javascript
Vue.JS入门教程之自定义指令
2016/12/08 Javascript
实例分析浏览器中“JavaScript解析器”的工作原理
2016/12/12 Javascript
Node.js应用设置安全的沙箱环境
2018/04/23 Javascript
vue项目中使用tinymce编辑器的步骤详解
2018/09/11 Javascript
JavaScript箭头函数中的this详解
2019/06/19 Javascript
webpack的tree shaking的实现方法
2019/09/18 Javascript
[01:44]Ti10举办地公布
2019/08/25 DOTA
python 提取文件的小程序
2009/07/29 Python
python 统计列表中不同元素的数量方法
2018/06/29 Python
利用Python将文本中的中英文分离方法
2018/10/31 Python
基于python实现高速视频传输程序
2019/05/05 Python
Python3+Pycharm+PyQt5环境搭建步骤图文详解
2019/05/29 Python
Python适配器模式代码实现解析
2019/08/02 Python
浅析matlab中imadjust函数
2020/02/27 Python
浅谈keras使用中val_acc和acc值不同步的思考
2020/06/18 Python
python 读取、写入txt文件的示例
2020/09/27 Python
CSS3制作日历实现代码
2012/01/21 HTML / CSS
不开辟用于交换数据的临时空间,如何完成字符串的逆序
2012/12/02 面试题
银行实习的自我鉴定
2013/12/10 职场文书
2014爱耳日宣传教育活动总结
2014/03/09 职场文书
预备党员转正思想汇报
2014/09/26 职场文书
2015年员工工作总结范文
2015/04/08 职场文书
导游词之张家口
2019/12/13 职场文书
Golang 如何实现函数的任意类型传参
2021/04/29 Golang