探究装饰器参数

编写传参的装饰器

通常我们见到的简单装饰器这样的:

import json
import functools def json_output(func):
@functools.wraps(decorated)
def inner(*args, **kwargs):
result = func(*args, **kwargs)
return json.dumps(result)
return inner @json_output
def f():
return {'status': 'done'}

当装饰器应用于函数 f 上时,它接受 f 作为其参数,返回一个函数 inner ,且将他绑定到变量f上。

示例中我们编写的装饰器 json_output 只接受一个隐式参数——即被装饰的方法,在使用此装饰器时本身看上去是并没有参数的。然而有时候需要让装饰器自身带有一些需要的信息,从而使装饰器可以使用恰当的方式装饰方法。比如上面的例子中,我们想通过向装饰器传入不同的参数来控制输出结果的缩进(indent)和排序(sort)。我们可以这么做:

import json
import functools def json_output(indent=None, sort_keys=False):
def actual_decorator(func):
@functools.wraps(func)
def inner(*args, **kwargs):
result = func(*args, **kwargs)
return json.dumps(result, indent=indent, sort_keys=sort_keys)
return inner
return actual_decorator @json_output(indent=4)
def f():
return {'status': 'done'}

理解传参的装饰器

初次看起来会觉得比较绕人,因为函数里嵌套了两个函数定义,然而实际上和之前一个版本的区别在于为了接收json序列化的参数多包装了一层,所以

@json_output(indent=4)
def f():
return {'status': 'done'} # 相当于
@actual_decorator
def f():
return {'status': 'done'}

这样看起来就会明晰很多。

实际上, 装饰器里的 @ 后接收一个函数,该函数以被装饰的函数(例子中是f)为参数,并且返回一个函数。当需要在装饰函数的同时传入参数的话,那么就需要多包装一层,先传入参数(例子中是 indent=4 )返回一个装饰的函数(例子中是 actual_decorator ), 这个返回的的函数 就跟以前一样接受被装饰的函数(f)作为参数并且返回一个函数作为装饰最后的方法供调用。

传参和不传参的兼容

然而当我们像上面那样定义装饰器时,就不能这样调用:

import json
import functools def json_output(indent=None, sort_keys=False):
def actual_decorator(func):
@functools.wraps(func)
def inner(*args, **kwargs):
result = func(*args, **kwargs)
return json.dumps(result, indent=indent, sort_keys=sort_keys)
return inner
return actual_decorator @json_output
def f():
return {'status': 'done'}

在实际的项目过程中,有时会出现这样的状况: 一开始写的装饰器时不需要使用时传参数的,后来发现有必要传参数,改好后原来不传参的装饰器不能正常使用了,这是修改原来使用的地方是项痛苦的事情。

这时候就需要对装饰器做一个兼容,使它在以下情况都可用:

@json_output
@json_output()
@json_output(indent=4)

具体做法如下:

import json
import functools def json_output(decorated_=None, indent=None, sort_keys=False):
if decorated_ and (indent or sort_keys):
raise def actual_decorator(func):
@functools.wraps(func)
def inner(*args, **kwargs):
result = func(*args, **kwargs)
return json.dumps(result, indent=indent, sort_keys=sort_keys)
return inner
if decorated_:
return actual_decorator(decorated_)
else:
return actual_decorator @json_output(indent=4)
def f1():
return {'status': 'done'} @json_output
def f2():
return {'status': 'done'} @json_output()
def f3():
return {'status': 'done'} print f1()
print f2()
print f3()

代码中关键的地方在于 json_output 在最后对参数 decorated 进行了判断,有的话证明是不传参调用,那么直接返回 actual_decorator 的调用;没有的话则代表是传参类型的调用(虽然参数可能不存在),那么返回 actual_decorator 。其中有点需要注意, josn_output 的传参需要使用关键字参数,如果像下面这样直接传一个位置参数,那么根据现在的实现会出现错误(因为它会被当成 decorated_ )。

@json_output(4)  #错误的使用方法
def f4():
return {'status': 'done'}

参考资料

  • 《Python高级编程》 by Luke Sneeriger

Python装饰器探究——装饰器参数的更多相关文章

  1. python装饰器探究与参数的领取

    首先上原文, 现在,假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为"装饰器" ...

  2. Python装饰器AOP 不定长参数 鸭子类型 重载(三)

    1 可变长参数与关键字参数 *args代表任意长度可变参数 **kwargs代表关键字参数 用*args和**kwargs只是为了方便并没有强制使用它们. 缺省参数即是调用该函数时,缺省参数的值若未被 ...

  3. python——装饰器(不定长参数,闭包,装饰器)示例

    def func(functionName): print("正在装饰") def func_in(*args, **kargs): print("------func_ ...

  4. Python 第五天 装饰器

    装饰器 装饰器是函数,只不过该函数可以具有特殊的含义,装饰器用来装饰函数或类,使用装饰器可以在函数执行前和执行后添加相应操作. def wrapper(func): def result(): pri ...

  5. Python中利用函数装饰器实现备忘功能

    Python中利用函数装饰器实现备忘功能 这篇文章主要介绍了Python中利用函数装饰器实现备忘功能,同时还降到了利用装饰器来检查函数的递归.确保参数传递的正确,需要的朋友可以参考下   " ...

  6. python函数与方法装饰器

    之前用python简单写了一下斐波那契数列的递归实现(如下),发现运行速度很慢. def fib_direct(n): assert n > 0, 'invalid n' if n < 3 ...

  7. Python中的各种装饰器详解

    Python装饰器,分两部分,一是装饰器本身的定义,一是被装饰器对象的定义. 一.函数式装饰器:装饰器本身是一个函数. 1.装饰函数:被装饰对象是一个函数 [1]装饰器无参数: a.被装饰对象无参数: ...

  8. python基础5之装饰器

    内容概要: 一.装饰器前期知识储备 1.python解释函数代码过程: python解释器从上往下顺序解释代码,碰到函数的定义代码块不会立即执行它,而是将其放在内存中,等到该函数被调用时,才执行其内部 ...

  9. python函数式编程之装饰器(一)

    1.开放封闭原则 简单来说,就是对扩展开放,对修改封闭 在面向对象的编程方式中,经常会定义各种函数. 一个函数的使用分为定义阶段和使用阶段,一个函数定义完成以后,可能会在很多位置被调用 这意味着如果函 ...

随机推荐

  1. Cloud Computing

    More numbers, More power. We waste much more every day. Everything can be connectible through specia ...

  2. php的yii框架开发总结1

    最近用php的yii框架写了一个小的demo,虽然不复杂,但是也学习了很多东西,现在总结一下. 项目需求:为几个教研室写一个日报系统,每个人每天写日报,并且系统有自动实现发邮件功能. 额外要求:1.人 ...

  3. win10下各种问题的解决办法

    本来申请这个博客是为了写一些Java学习笔记的,但是鉴于我半年内无数次重装系统的惨痛经历,所以把win10系统的一些问题总结一下. 此账号密码:1994llz. 1.win10取消开机密码: http ...

  4. 什么是 pwd

    pwd print work directory, 指linux terminal的当前目录 $ pwd

  5. ring0 SSDTHook

    SSDT 的全称是 System Services Descriptor Table,系统服务描述符表.这个表就是一个把 Ring3 的 Win32 API 和 Ring0 的内核 API 联系起来. ...

  6. 我的visual studio 配色方案 Rubik c++版

    只是更改了c++的配色,放出来与大家分享,因为大胆地采用了各种颜色,所以我把它取名叫做Rubik,因为Rubik‘s cube也就是魔方,我本人是非常喜欢魔方的,然后也符合颜色丰富多彩的这个特征,希望 ...

  7. Socket的基本使用步骤

    Socket的基本使用步骤 一.使用Socket,首先需要导入这几个系统头文件 #import <sys/socket.h> #import <netinet/in.h> #i ...

  8. G711格式语音采集/编码/转码/解码/播放

    2019-05-01 语音g711格式和AMR格式类似,应用很简单,很多人已经整理过了,收录于此,以备不时之需,用别人现成的足矣,我们的时间应该用来干更有意义的事. 1.PCM to G711 Fas ...

  9. DevExpress控件经验集合

    关于GridControl的可以先看这里:http://blog.csdn.net/dong413876225/article/details/8313094 增加新行,我用了AddNewRow,但是 ...

  10. P2447 [SDOI2010]外星千足虫

    怎么说呢? 因为是在mod 2 意义下的吗(一般是遇到二就可能是位运行算或二分图) 就可以利用异或计算. 因为奇数和偶数在二进制上就用判断最后一位就可以了 然后因为异或符合交换律和结合律 直接消元就可 ...