说起装饰器我们可能已经很熟悉了(不了解的可以查看python基础学习——装饰器),随手就可以写一个简单的装饰器


def decorator(func):
def inner(*args, **kwargs):
# 执行函数前做点事
result = func(*args, **kwargs)
# 执行函数后干点啥
return result
return inner

但是装饰器的实现可不是只有这一种,请看下面

通过类来实现

上面写的装饰器是由函数的方式实现的,但我们也可以用类的方式写一个同样的装饰器,还记得call方法吗


class decorator:
def __init__(self,func):
self.func = func def __call__(self, *args, **kwargs):
# 执行函数前做点事
result = self.func(*args, **kwargs)
# 执行函数后干点啥
return result

比如有一个add()函数被装饰,其实相当于add = decorator(add),那么decorator(add)就是这个类的实例, 执行add(a,b)的时候就是执行call方法了

参数化的装饰器

在装饰器中,我们在被装饰函数执行前会做一些事,在被装饰函数执行后可能也会做一些事,可是按照上面的写法,这些事情都是固定写好的,可不可以让这些动作会自定义的做些区别变化,那么参数化的装饰器能够满足这个要求


def decorator(what_i_say='我什么也没说'):
def actual_decorator(func):
def inner(*args, **kwargs):
# 执行函数前做点事
print(what_i_say)
result = func(*args, **kwargs)
# 执行函数后干点啥
return result
return inner
return actual_decorator @decorator('我要做一次加法')
def add(a, b):
return a + b print(add(3, 9))

有了这个参数化装饰器,我们就可以在装饰函数时将参数写在括号内,也就是传入参数(这里我传的是字符串,其实没啥意义,只是为了简单表示参数)

但是我们看到这个装饰器的实现中出现了三次def,之前的不都是出现两次吗,这次怎么出现了3次?它是怎么执行的

其实稍微想一下就想通了,还是按照装饰的过程来

之前是add = decorator(add),这次就把decorator换成decorator(‘我要做一次加法’),add = decorator(‘我要做一次加法’)(add),由于函数是一等对象,所以decorator(‘我要做一次加法’)就是装饰器中第一次返回的actual_decorator,由于闭包的特性,传递的参数绑定到了内部函数中,所以decorator执行完后,传入的’我要做一次加法’还是可以打印出来。

所以这个参数化的装饰器和普通的装饰器装饰的过程是一样的。

保存函数元数据的装饰器

在使用装饰器后一段时间,可能有一天我们会突然发现一点不对劲的地方,比如我们打印被装饰的函数的时候


print(add)
#<function decorator.<locals>.actual_decorator.<locals>.inner at 0x00000211E632CB70>

咦,不对啊,怎么它的名字不是add而是decorator..actual_decorator..inner这个玩意。我们仔细一读想起来了,这是被装饰器装饰了的那个函数,所以它把装饰器里的那个给打印出来了。可是把这个玩意打印出来干嘛呀,我要原来的函数名,有没有什么办法啊,办法当然有,如下


from functools import wraps def decorator(what_i_say):
def actual_decorator(func): @wraps(func)
def inner(*args, **kwargs):
# 执行函数前做点事
print(what_i_say)
result = func(*args, **kwargs)
# 执行函数后干点啥
return result
return inner
return actual_decorator

我们从functools模块中引入了wraps()装饰器,用它来装饰装饰器中的inner函数,这样再次使用这个装饰器去装饰函数,它的元数据就保留下来了


@decorator('我要做一次加法')
def add(a, b):
return a + b print(add) # <function add at 0x000001F5DEE966A8>

最新文章

  1. setInterval的停止与启动
  2. 理解记忆三种常见字符编码:ASCII, Unicode,UTF-8
  3. 如何在真机装linux(本人在台式机上又添了个硬盘)
  4. Nginx报错403 forbidden (13: Permission denied)的解决办法
  5. AOP理解
  6. 《浅谈磁盘控制器驱动》,磁盘控制器驱动答疑解惑![2012.1.29完结]by skyfree
  7. Linux 升级 Python 至 3.x
  8. Sql Server 常用的查询
  9. js中location.search、split()HTML5中localStorage
  10. why dicePlayer cannot player with defy mb526
  11. [转]win7 64位下android开发环境的搭建
  12. 【BZOJ2434-[Noi2011]】阿狸的打字机(AC自动机(fail树)+离线+树状数组)
  13. HOJ1014
  14. 翻译一篇文章:It&#39;s Difficult to Grow a Test Developer(成为测试开发工程师的艰辛)
  15. wpf通过VisualTreeHelper找到控件内所有CheckBox和TextBox并做相应绑定
  16. luogu【P2753】[USACO4.3]字母游戏Letter Game
  17. [SDOI 2015]约数个数和
  18. 干货|爱奇艺CDN巡检系统技术解析
  19. @Conditional 原理
  20. Educational Codeforces Round 51 (Rated for Div. 2) G. Distinctification(线段树合并 + 并查集)

热门文章

  1. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十五):系统服务监控
  2. Python sqlalchemy的基本使用
  3. 通过 CLI 管理 Jenkins Server
  4. EF SaveChanges() 报错(转载)
  5. 修改mysql忽略大小写
  6. WebApi接口传参
  7. windows server 2008 R2 开启远程桌面
  8. [PHP] 算法-根据前序和中序遍历结果重建二叉树的PHP实现
  9. 性能监控(2)–linux下的vmstat命令
  10. Git 实战手册(一): 批量修改log中的提交信息