python中的装饰器

装饰器是为了解决以下描述的问题而产生的方法

我们在已有的函数代码的基础上,想要动态的为这个函数增加功能而又不改变原函数的代码

例如有三个函数:

  1. def f1(x):
  2. return x
  3. def f2(x):
  4. return x*x
  5. def f3(x):
  6. return x*x*x

而我们想为这三个函数增加一个函数调用打印功能 类似print("call f1()")

如果我们直接修改的话,需要对每个函数的内部进行改写。

所以为了简化代码,我们可以使用python内置的@装饰器的方法,可以做到修饰函数的功能

Python的 decorator 本质上就是一个高阶函数,它接收一个函数作为参数,然后,返回一个新函数。

装饰器可以极大地简化代码,避免每个函数写重复性代码

不带参数的decorator

例如我们可以编写一个@log可以打印函数调用的装饰器

  1. def log(f):
  2. def fn(x):
  3. print ('call ' + f.__name__ + '()...')
  4. return f(x)
  5. return fn

然后我们可以测试一下

  1. @log
  2. def f1(x):
  3. return x
  4.  
  5. a=f1(1)
  1. print(a)

结果

  1. call f1()...
  2. 1

但是,对于参数不是一个的函数,调用将报错:

  1. @log
  2. def add(x, y):
  3. return x + y
  4. print (add(1, 2))
  1. Traceback (most recent call last):
  2. File "D:/pythonwork/cvworktest/final/practice.py", line 12, in <module>
  3. print (add(1, 2))
  4. TypeError: fn() takes 1 positional argument but 2 were given

这是因为add() 函数需要传入两个参数,但是 @log 写死了只含一个参数的返回函数。

要让 @log 自适应任何参数定义的函数,可以利用Python的 *args 和 **kw,保证任意个数的参数总是能正常调用:

  1. def log(f):
  2. def fn(*args, **kw):
  3. print ('call ' + f.__name__ + '()...')
  4. return f(*args, **kw)
  5. return fn

现在,对于任意函数,@log 都能正常工作。

带参数decorator

上面的@log不带任何参数,同样我们可以编写一个带参数的decorator

例如如果有的函数非常重要,希望打印出'[INFO] call xxx()...',有的函数不太重要,希望打印出'[DEBUG] call xxx()...',这时

log函数本身就需要传入'INFO'或'DEBUG'这样的参数,类似这样:

  1. @log('DEBUG')
  2. def my_func():
  3. pass

我们把它翻译成高阶函数就是这样:

  1. my_func = log('DEBUG')(my_func)

展开:

  1. log_decorator = log('DEBUG')
  2. my_func = log_decorator(my_func)

又相当于:

  1. log_decorator = log('DEBUG')
  2. @log_decorator
  3. def my_func():
  4. pass

所以,带参数的log函数首先返回一个decorator函数,再让这个decorator函数接收my_func并返回新函数:

  1. def log(content):
  2. def log_decorator(f):
  3. def fn(*args, **kw):
  4. print ('[%s] %s()...' % (content, f.__name__))
  5. return f(*args, **kw)
  6. return fn
  7. return log_decorator
  8.  
  9. @log('DEBUG')
  10. def test():
  11. pass
  12. print (test())

结果:

  1. [DEBUG] test()...
  2. None

decorator的注意事项:

经过@decorator“改造”后的函数,和原函数相比,除了功能多一点外,还有很重要的一点就是函数自身的改变

在没有decorator的情况下,打印函数名:

  1. def f1(x):
  2. pass
  3. print (f1.__name__)

结果:

  1. f1

有decorator的情况下,再打印函数名:

  1. def log(f):
  2. def wrapper(*args, **kw):
  3. print ('call...')
  4. return f(*args, **kw)
  5. return wrapper
  6. @log
  7. def f2(x):
  8. pass
  9. print (f2.__name__)

结果:

  1. wrapper

由于decorator返回的新函数函数名已经不是'f2',而是@log内部定义的'wrapper'。这对于那些依赖函数名的代码就会失效。decorator还改变了函数的__doc__等其它属性。如果要让调用者看不出一个函数经过了@decorator的“改造”,就需要把原函数的一些属性复制到新函数中:

  1. def log(f):
  2. def wrapper(*args, **kw):
  3. print ('call...')
  4. return f(*args, **kw)
  5. wrapper.__name__ = f.__name__
  6. wrapper.__doc__ = f.__doc__
  7. return wrapper

这样写decorator很不方便,因为我们也很难把原函数的所有必要属性都一个一个复制到新函数上,所以Python内置的functools可以用来自动化完成这个“复制”的任务:

  1. import functools
  2. def log(f):
  3. @functools.wraps(f)
  4. def wrapper(*args, **kw):
  5. print ('call...')
  6. return f(*args, **kw)
  7. return wrapper

注意:对于函数的参数信息哦我们无法确定,因为装饰器与原函数的参数名不一定一样

python中的装饰器decorator的更多相关文章

  1. 浅析python中的装饰器decorator

    最近学习python,其中decorator比较难理解,遂写一篇来总结供后续查阅. 定义一个函数,想在运行时动态的改变函数的功能,又不想改变函数本身的代码,可以使用高阶函数(可以使用函数作为参数) 装 ...

  2. [转] Python中的装饰器(decorator)

    想理解Python的decorator首先要知道在Python中函数也是一个对象,所以你可以 将函数复制给变量 将函数当做参数 返回一个函数 函数在Python中和变量的用法一样也是一等公民,也就是高 ...

  3. 简单说明Python中的装饰器的用法

    简单说明Python中的装饰器的用法 这篇文章主要简单说明了Python中的装饰器的用法,装饰器在Python的进阶学习中非常重要,示例代码基于Python2.x,需要的朋友可以参考下   装饰器对与 ...

  4. 写python中的装饰器

    python中的装饰器主要用于在已有函数实现功能前附加需要输出的信息,下面将用实例展示我如何写装饰器. 首先分别尝试写装饰器装饰一个无参函数和一个有参函数(被装饰函数仅输出,无返回值情况下) def ...

  5. 【Python】python中的装饰器——@

    对装饰器本来就一知半解的,今天终于弄清楚了,Python中的装饰器是对装饰者模式的很好运用,简化到骨子里了. python中为什么需要装饰器,看这里:http://www.cnblogs.com/hu ...

  6. Python 中实现装饰器时使用 @functools.wraps 的理由

    Python 中使用装饰器对在运行期对函数进行一些外部功能的扩展.但是在使用过程中,由于装饰器的加入导致解释器认为函数本身发生了改变,在某些情况下——比如测试时——会导致一些问题.Python 通过  ...

  7. python中@property装饰器的使用

    目录 python中@property装饰器的使用 1.引出问题 2.初步改善 3.使用@property 4.解析@property 5.总结 python中@property装饰器的使用 1.引出 ...

  8. python语法32[装饰器decorator](转)

    一 装饰器decorator decorator设计模式允许动态地对现有的对象或函数包装以至于修改现有的职责和行为,简单地讲用来动态地扩展现有的功能.其实也就是其他语言中的AOP的概念,将对象或函数的 ...

  9. python 语法之 装饰器decorator

    装饰器 decorator 或者称为包装器,是对函数的一种包装. 它能使函数的功能得到扩充,而同时不用修改函数本身的代码. 它能够增加函数执行前.执行后的行为,而不需对调用函数的代码做任何改变. 下面 ...

随机推荐

  1. 如何去掉边框及input的兼容问题?

    右偷个懒,发现别人写的也不错,我就做个小搬运工 如何去掉边框及input的兼容问题? 说到input,又不得不说它的兼容问题.input如何兼容各个浏览器呢? 第一步:清除input的border的默 ...

  2. JavaScript学习——完善注册页面表单校验

    1.之前我们已经使用弹出框的方式实现了表单校验的功能,但是此种方式用户体验效果很差 我们希望做成把提示信息和校验结果放在输入栏的后面. 2.步骤分析 (此案例基于HTML&CSS——网站注册页 ...

  3. mongodb报错:connection refused because too many open connections: 819

    问题: 发现mongodb无法连接,查看mongodb日志,出现大量的如下报错: [initandlisten] connection refused because too many open co ...

  4. GUI 图形用户界面 [学习笔记]

    今晚返璞归真了一把, 系统了解了一下GUI的有关知识: GUI(Graphical User Interface) 图形用户界面 是指采用图形方式显示的计算机操作用户接口.与早期计算机使用的命令行界面 ...

  5. 2013-11-02 【webrebuild广州站】分享会纪要

    为了不让自己沉浸个人的技术研究当中,也为了多去接触业界新技术新思想,今天去参加了webrebuild广州站的一个分享交流会,效果不错,有一些获益.听了四个主题,依据个人获取信息的情况来做个纪要(比较粗 ...

  6. Aspose WorkbookDesigner打开文件异常"Error xml namespace"

    错误描述: 平台是VS2010的.Net Framework 需要用Aspose的WorkbookDesigner打开excel文件的时候产生异常 异常码是Aspose.Cells.Exception ...

  7. docker私有仓库yum安装 docker-registry

    [root@riyimei-node1:/root]> yum install docker-registryLoaded plugins: fastestmirror, langpacksLo ...

  8. 紫书 例题 11-3 UVa 1151 (有边集的最小生成树+二进制枚举子集)

    标题指的边集是说这道题的套餐, 是由几条边构成的. 思路是先做一遍最小生成树排除边, 因为如果第一次做没有加入的边, 到后来新加入了很多权值为0的边,这些边肯定排在最前面,然后这条边的前面的那些边肯定 ...

  9. Java基础学习总结(5)——多态

    一.面向对象最核心的机制--动态绑定,也叫多态 1.1.通过下面的例子理解动态绑定,即多态 package javastudy.summary; class Animal { /** * 声明一个私有 ...

  10. jquery-ui日期时间控件实现

    日期控件和时间控件为独立控件,日期时间控件要同一时候导入日期控件和时间控件的js,然后在日期控件加入时间控件显示參数,没有导入时间控件js.日期控件函数设置的时间控件參将包错 日期控件官网网址:htt ...