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 相关文章推荐
利用Django框架中select_related和prefetch_related函数对数据库查询优化
Apr 01 Python
Python中isnumeric()方法的使用简介
May 19 Python
Python使用dis模块把Python反编译为字节码的用法详解
Jun 14 Python
Python实现更改图片尺寸大小的方法(基于Pillow包)
Sep 19 Python
python利用urllib和urllib2访问http的GET/POST详解
Sep 27 Python
基于Python实现用户管理系统
Feb 26 Python
Python操作MySQL数据库的两种方式实例分析【pymysql和pandas】
Mar 18 Python
Django Channels 实现点对点实时聊天和消息推送功能
Jul 17 Python
使用Python串口实时显示数据并绘图的例子
Dec 26 Python
python3连接MySQL8.0的两种方式
Feb 17 Python
Python递归调用实现数字累加的代码
Feb 25 Python
python集合能干吗
Jul 19 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
VPS中使用LNMP安装WordPress教程
2014/12/28 PHP
php检查函数必传参数是否存在的实例详解
2017/08/28 PHP
PHP命名空间namespace及use的简单用法分析
2018/08/03 PHP
PHP读取XML文件的方法实例总结【DOMDocument及simplexml方法】
2019/09/10 PHP
jquery实现tr元素的上下移动示例代码
2013/12/20 Javascript
JavaScript定义类的几种方式总结
2014/01/06 Javascript
控制input输入框中提示信息的显示和隐藏的方法
2014/02/12 Javascript
文本框水印提示效果的简单实现代码
2014/02/22 Javascript
JavaScript中变量声明有var和没var的区别示例介绍
2014/09/15 Javascript
两种方法基于jQuery实现IE浏览器兼容placeholder效果
2014/10/14 Javascript
jquery实现带缩略图的全屏图片画廊效果实例
2015/06/25 Javascript
php结合imgareaselect实现图片裁剪
2015/07/05 Javascript
Javascript编程之继承实例汇总
2015/11/28 Javascript
3种vue组件的书写形式
2017/11/29 Javascript
详解JS模块导入导出
2017/12/20 Javascript
Node实战之不同环境下配置文件使用教程
2018/01/02 Javascript
Vue-cli项目获取本地json文件数据的实例
2018/03/07 Javascript
Vue中如何实现proxy代理
2018/04/20 Javascript
vue动画—通过钩子函数实现半场动画操作
2020/08/09 Javascript
[38:44]DOTA2上海特级锦标赛A组小组赛#2 Secret VS CDEC第二局
2016/02/25 DOTA
Python argv用法详解
2016/01/08 Python
Python的Flask框架中使用Flask-Migrate扩展迁移数据库的教程
2016/06/14 Python
Python实现的文本简单可逆加密算法示例
2017/05/18 Python
python如何读写csv数据
2018/03/21 Python
python从入门到精通 windows安装python图文教程
2019/05/18 Python
python 字典操作提取key,value的方法
2019/06/26 Python
python自定义函数实现最大值的输出方法
2019/07/09 Python
django model通过字典更新数据实例
2020/04/01 Python
Python 中如何使用 virtualenv 管理虚拟环境
2021/01/21 Python
瑞士香水购物网站:Parfumcity.ch
2017/01/14 全球购物
Crabtree & Evelyn欧盟:豪华洗浴、身体和护发
2021/03/09 全球购物
简单租房协议书(范本)
2014/10/13 职场文书
2019新员工试用期转正申请书3篇
2019/08/13 职场文书
Python深度学习之实现卷积神经网络
2021/06/05 Python
Mysql binlog日志文件过大的解决
2021/10/05 MySQL
Tomcat starup.bat 脚本实现开机自启动
2022/04/20 Servers