探究装饰器参数

编写传参的装饰器

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

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. TX Text Control X10新特性之图像占位符合并

    文档处理控件TX Text Control即将发布的X10版本,将升级重点还是放到了其比较优势的流式布局报表设计和生成上.慧都获得了来自其开发商Text Control GmbH公司的一手资料,迫不及 ...

  2. 【工作】Proxy Server的优化 - 检测目标网站URL变化

    在工作中,我在组里负责一个Proxy(代理)的Module,这个Module是针对微软的Office 365的邮件门户OWA实现,工作起来后,用户访问Office 365 OWA,无需再输入Offic ...

  3. sharepoint国内网站一览表(转发)

    中国石油化工集团公司http://www.sinopecgroup.com/Pages/index.aspx () 中国南方航空http://group.csair.com/_layouts/grou ...

  4. 开源框架 epics,开源画面编辑软件 edm

    epics Experimental Physics and Industrial Control System 一套开源软件框架,实验物理和工业控制系统 http://www.aps.anl.gov ...

  5. Selenium入门11 滚动条控制(通过js)

    这一节要有js基础.做web端的UI自动化必须要有html,css,javascript前端基础. 滚动条控制: 1 移动垂直滚动条 document.documentElement.scrollTo ...

  6. node入口文件分析和目录初始化

    1.需要安装的模块 npm install express npm install jade npm install mongoose npm install bower -g npm install ...

  7. Java继承和访问修饰符

    继承 概念:为了提取两个类中公共代码,可以使用继承抽取重复性的代码到一个公共类中,这个公共的类称为父类(super class).继承于父类的类称为子类(sub class). 关键字     ext ...

  8. P1909 买铅笔

    题目描述 P老师需要去商店买n支铅笔作为小朋友们参加NOIP的礼物.她发现商店一共有 33种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同.为了公平起 见,P老师决定只买同一种包装的铅笔 ...

  9. 用dynamic的方式来转换Json对象

    来自这里:http://stackoverflow.com/questions/3142495/deserialize-json-into-c-sharp-dynamic-object If you ...

  10. Ubuntu ndk环境变量配置

    https://blog.csdn.net/gulingfengze/article/details/70149092 用source /etc/profile,有些博客写的使用sudo gedit ...