Python中装饰器兼容加括号和不加括号的写法详解

yipeiwu_com5年前Python基础

使用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),再加上一个装饰器的装饰器,会有点理解困难。如果理解不了,最好不要对着网上的博文(包括本文)企图格物致知了,多去看看代码,多写一写。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对【听图阁-专注于Python设计】的支持。

参考资料

How to create a Python decorator that can be used either with or without parameters?

相关文章

python实现自动解数独小程序

跟朋友最近聊起来数独游戏,突发奇想使用python编写一个自动计算数独解的小程序。 数独的规则不再过多阐述,在此描述一下程序的主要思路: (当前程序只针对于简单的数独,更复杂的还待深入挖...

解决pycharm的Python console不能调试当前程序的问题

使用python时,程序能运行,但是不能调试,找了半天解决方法,最后此操作分分钟奏效。 两种方法: 方法一:选中要运行的代码,右键Execute Selection in Console...

python K近邻算法的kd树实现

k近邻算法的介绍 k近邻算法是一种基本的分类和回归方法,这里只实现分类的k近邻算法。 k近邻算法的输入为实例的特征向量,对应特征空间的点;输出为实例的类别,可以取多类。 k近邻算法...

Django验证码的生成与使用示例

Django验证码的生成与使用示例

前言 本文主要介绍的是关于Django验证码生成与使用的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍: 方法如下: 1、基于PIL生成一个带验证码的图片和验证码,生成验证码...

Python常用字符串替换函数strip、replace及sub用法示例

本文实例讲述了Python常用字符串替换函数strip、replace及sub用法。分享给大家供大家参考,具体如下: 今天在做一道今年秋季招聘题目的时候遇上了一个替换的问题,题目看起来好...