---1---

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

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

---2---

本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator.

观察下面的代码:

def log(func):def wrapper(*args, **kw):print('call %s():' % func.__name__)return func(*args, **kw)return wrapper
@log
def now():print('2015-3-25')

@log放到now()函数的定义处,相当于执行了语句:

now = log(now)

由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

---3---

如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:

def log(text):def decorator(func):def wrapper(*args, **kw):print('%s %s():' % (text, func.__name__))return func(*args, **kw)return wrapperreturn decorator

这个3层嵌套的decorator用法如下:

@log('execute')
def now():print('2015-3-25')

和两层嵌套的decorator相比,3层嵌套的效果是这样的:

>>> now = log('execute')(now)

我们来剖析上面的语句,首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

以上两种decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper'

>>> now.__name__
'wrapper'

因为返回的那个wrapper()函数名字就是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:

import functoolsdef log(func):@functools.wraps(func)def wrapper(*args, **kw):print('call %s():' % func.__name__)return func(*args, **kw)return wrapper

或者针对带参数的decorator:

import functoolsdef log(text):def decorator(func):@functools.wraps(func)def wrapper(*args, **kw):print('%s %s():' % (text, func.__name__))return func(*args, **kw)return wrapperreturn decorator

上面的东西看的一知半解的,看完下面的这一段应该会好点:

def check_is_admin(f):def wrapper(*args,**kwargs):if kwargs.get('username') != 'admin':raise Exception("This user is not allowed to get food")return f(*args, **kwargs)return wrapperclass Storage(object):@check_is_admindef get_food(self, username,food):return Storage.get(food)@check_is_admindef put_food(self,username,food):return Storage.put(food)

参考:

(1).https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584(廖雪峰官方文档)

(2).https://www.zhihu.com/question/21408921/answer/129036707 (怎样才能写出 Pythonic 的代码?)

(3).https://www.zhihu.com/question/26930016/answer/99243411   (如何理解Python装饰器?)

python中的装饰器-(重复阅读)相关推荐

  1. 为什么说想到Python中的装饰器是天才

    为什么说想到Python中的装饰器是天才 只需一个@符号就能分析.测试和重复使用你的代码 带着魔杖的仙女在Python代码中飞舞 软件中有没有什么是神奇的小魔法? 有,装饰器却非常接近! 如果说有一件 ...

  2. python类装饰器详解-python 中的装饰器详解

    装饰器 闭包 闭包简单的来说就是一个函数,在该函数内部再定义一个函数,并且这个内部函数用到了外部变量(即是外部函数的参数),最终这个函数返回内部函数的引用,这就是闭包. def decorator(p ...

  3. python装饰器原理-python 中的装饰器及其原理

    装饰器模式 此前的文章中我们介绍过装饰器模式: 装饰器模式中具体的 Decorator 实现类通过将对组建的请求转发给被装饰的对象,并在转发前后执行一些额外的动作来修改原有的部分行为,实现增强 Com ...

  4. python中的装饰器decorator

    python中的装饰器 装饰器是为了解决以下描述的问题而产生的方法 我们在已有的函数代码的基础上,想要动态的为这个函数增加功能而又不改变原函数的代码 例如有三个函数: def f1(x):return ...

  5. python编写装饰器_写python中的装饰器

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

  6. python中的装饰器(基础装饰器)

    文章目录 一 前置知识-高阶函数,闭包 1. 高阶函数 2. 闭包 二 函数装饰器 1. 什么是装饰器(原理)? 2. 装饰器的实现 3. 何时执行装饰器 4. wraps方法 三 类装饰器 一 前置 ...

  7. [转载]理解PYTHON中的装饰器

    [翻译]理解PYTHON中的装饰器 来源stackoverflow上的问题 链接 python的函数是对象 要理解装饰器,首先,你必须明白,在python中,函数是对象. 这很重要. 简单例子来理解为 ...

  8. python中的装饰器有哪些-python 装饰器以及开发中常用的例子

    有时候我们想为多个函数,同意添加某一种功能,比如及时统计,记录日志,缓存运算结果等等,而又不想改变函数代码 那就定义装饰器函数,用它来生成一个在原函数基础添加了新功能的函数,代替原函数 参考金角大王的 ...

  9. python中的装饰器怎么运行_Python 装饰器入门(上)

    翻译前想说的话: 这是一篇介绍python装饰器的文章,对比之前看到的类似介绍装饰器的文章,个人认为无人可出其右,文章由浅到深,由函数介绍到装饰器的高级应用,每个介绍必有例子说明.文章太长,看完原文后 ...

最新文章

  1. MongoDB,凉凉?
  2. Android 版本适配:9.0 Pie(API 级别 28)
  3. 【免费毕设】ASP.NET猜数游戏的设计与开发(源代码+lunwen)
  4. 常见的算法题目分类图
  5. 值类型和引用类型的区别?
  6. Java Thread.yield详解
  7. BAT 文件 常用 代码 大全
  8. java jsp交友系统_Jsp+Servlet+Javabean网络交友程序设计+源代码
  9. Qt网络编程-简易版UDP单播通信入门Demo(3)
  10. 安装 Northwind 示例数据库
  11. Win10系统磁盘分区图文教程
  12. Redis zset的zadd()和zincrby()踩坑记录
  13. 资源管理Placement部署(Nova)
  14. 用mpx框架自定义小程序底部tabbar
  15. 如何临时修改ip地址,永久修改ip地址
  16. STM32自动生成精美图案
  17. 安卓免ROOT卸载预装应用程序简要流程
  18. 解决 Mysql 1366 错误
  19. EditPlus 处理中文乱码
  20. 电信无线猫连接无线路由器

热门文章

  1. 拉取ftp服务器上的文件_winscp和云服务器,2步实现winscp将文件上传到腾讯云Linux云服务器...
  2. 普通调幅(AM)与包络检波(matlab实现)
  3. 2018.08.02 hdu1558 Segment set(并查集+计算几何)
  4. ServletConfig的详解
  5. 简单的反射 把datatable 转换成list对象
  6. SQL学习笔记之存储过程的编写
  7. matlab z变换离散化_用C++编写一个简单的光栅化渲染器:3D篇
  8. 最常用计算机信息呼唤标准代码,计算机考试题
  9. win10计算机跑分,鲁大师如何跑分_鲁大师跑分详细教程
  10. oracle11g导出dmp文件 少表,Oracle11g导出dmp并导入Oracle10g的操作记录