python 装饰器(四):装饰器基础(三)叠放装饰器,参数化装饰器
叠放装饰器
示例 7-19 演示了叠放装饰器的方式:@lru_cache 应用到 @clock 装饰fibonacci 得到的结果上。在示例 7-21 中,模块中最后一个函数应用了两个 @htmlize.register 装饰器。
把 @d1 和 @d2 两个装饰器按顺序应用到 f 函数上,作用相当于 f =d1(d2(f))。
也就是说,下述代码:
@d1
@d2
def f():
print('f')
等同于:
def f():
print('f') f = d1(d2(f))
参数化装饰器
解析源码中的装饰器时,Python 把被装饰的函数作为第一个参数传给装饰器函数。那怎么让装饰器接受其他参数呢?答案是:创建一个装饰器
工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上。不明白什么意思?当然。下面以我们见过的最简单的装饰器
为例说明:示例 7-22 中的 register。
示例 7-22 示例 7-2 中 registration.py 模块的删减版,这里再次给出是为了便于讲解
registry = [] def register(func):
print('running register(%s)' % func)
registry.append(func)
return func @register
def f1():
print('running f1()') print('running main()')
print('registry ->', registry)
f1()
1 一个参数化的注册装饰器
为了便于启用或禁用 register 执行的函数注册功能,我们为它提供一个可选的 active 参数,设为 False 时,不注册被装饰的函数。实现方式参见示例 7-23。
从概念上看,这个新的 register 函数不是装饰器,而是装饰器工厂函数。调用它会返回真正的装饰器,这才是应用到目标函数上的装饰器。
示例 7-23 为了接受参数,新的 register 装饰器必须作为函数调用
registry = set() ➊
def register(active=True): ➋
def decorate(func): ➌
print('running register(active=%s)->decorate(%s)'
% (active, func))
if active: ➍
registry.add(func)
else:
registry.discard(func) ➎
return func ➏
return decorate ➐
@register(active=False) ➑
def f1():
print('running f1()')
@register() ➒
def f2():
print('running f2()') def f3():
print('running f3()')
❶ registry 现在是一个 set 对象,这样添加和删除函数的速度更快。
❷ register 接受一个可选的关键字参数。
❸ decorate 这个内部函数是真正的装饰器;注意,它的参数是一个函
数。
❹ 只有 active 参数的值(从闭包中获取)是 True 时才注册 func。
❺ 如果 active 不为真,而且 func 在 registry 中,那么把它删除。
❻ decorate 是装饰器,必须返回一个函数。
❼ register 是装饰器工厂函数,因此返回 decorate。
❽ @register 工厂函数必须作为函数调用,并且传入所需的参数。
❾ 即使不传入参数,register 也必须作为函数调用(@register()),即要返回真正的装饰器 decorate。
这里的关键是,register() 要返回 decorate,然后把它应用到被装饰的函数上。
示例 7-23 中的代码在 registration_param.py 模块中。如果导入,得到的结果如下:
>>> import registration_param
running register(active=False)->decorate(<function f1 at 0x10063c1e0>)
running register(active=True)->decorate(<function f2 at 0x10063c268>)
>>> registration_param.registry
{<function f2 at 0x10063c268>}
注意,只有 f2 函数在 registry 中;f1 不在其中,因为传给register 装饰器工厂函数的参数是 active=False,所以应用到 f1 上的 decorate 没有把它添加到 registry 中。
如果不使用 @ 句法,那就要像常规函数那样使用 register;若想把 f添加到 registry 中,则装饰 f 函数的句法是 register()(f);不想添加(或把它删除)的话,句法是 register(active=False)(f)。示例 7-24 演示了如何把函数添加到 registry 中,以及如何从中删除函数。
示例 7-24 使用示例 7-23 中的 registration_param 模块
>>> from registration_param import *
running register(active=False)->decorate(<function f1 at 0x10073c1e0>)
running register(active=True)->decorate(<function f2 at 0x10073c268>)
>>> registry # ➊
{<function f2 at 0x10073c268>}
>>> register()(f3) # ➋
running register(active=True)->decorate(<function f3 at 0x10073c158>)
<function f3 at 0x10073c158>
>>> registry # ➌
{<function f3 at 0x10073c158>, <function f2 at 0x10073c268>}
>>> register(active=False)(f2) # ➍
running register(active=False)->decorate(<function f2 at 0x10073c268>)
<function f2 at 0x10073c268>
>>> registry # ➎
{<function f3 at 0x10073c158>}
❶ 导入这个模块时,f2 在 registry 中。
❷ register() 表达式返回 decorate,然后把它应用到 f3 上。
❸ 前一行把 f3 添加到 registry 中。
❹ 这次调用从 registry 中删除 f2。
❺ 确认 registry 中只有 f3。
2 参数化clock装饰器
本节再次探讨 clock 装饰器,为它添加一个功能:让用户传入一个格式字符串,控制被装饰函数的输出。参见示例 7-25。
示例 7-25 clockdeco_param.py 模块:参数化 clock 装饰器
import time DEFAULT_FMT = '[{elapsed:0.8f}s] {name}({args}) -> {result}'
def clock(fmt=DEFAULT_FMT): ➊
def decorate(func): ➋
def clocked(*_args): ➌
t0 = time.time()
_result = func(*_args) ➍
elapsed = time.time() - t0
name = func.__name__
args = ', '.join(repr(arg) for arg in _args) ➎
result = repr(_result) ➏
print(fmt.format(**locals())) ➐
return _result ➑
return clocked ➒
return decorate ➓
if __name__ == '__main__': @clock()
def snooze(seconds):
time.sleep(seconds) for i in range(3):
snooze(.123)
❶ clock 是参数化装饰器工厂函数。
❷ decorate 是真正的装饰器。
❸ clocked 包装被装饰的函数。
❹ _result 是被装饰的函数返回的真正结果。
❺ _args 是 clocked 的参数,args 是用于显示的字符串。
❻ result 是 _result 的字符串表示形式,用于显示。
❼ 这里使用 **locals() 是为了在 fmt 中引用 clocked 的局部变量。
❽ clocked 会取代被装饰的函数,因此它应该返回被装饰的函数返回的值。
❾ decorate 返回 clocked。
❿ clock 返回 decorate。
⓫ 在这个模块中测试,不传入参数调用 clock(),因此应用的装饰器使用默认的格式 str。
在 shell 中运行示例 7-25,会得到下述结果:
$ python3 clockdeco_param.py
[0.12412500s] snooze(0.123) -> None
[0.12411904s] snooze(0.123) -> None
[0.12410498s] snooze(0.123) -> None
示例 7-26 和示例 7-27 是另外两个模块,它们使用了 clockdeco_param
模块中的新功能,随后是两个模块输出的结果。
示例 7-26 clockdeco_param_demo1.py
import time
from clockdeco_param import clock @clock('{name}: {elapsed}s')
def snooze(seconds):
time.sleep(seconds) for i in range(3):
snooze(.123)
示例 7-26 的输出:
$ python3 clockdeco_param_demo1.py
snooze: 0.12414693832397461s
snooze: 0.1241159439086914s
snooze: 0.12412118911743164s
示例 7-27 clockdeco_param_demo2.py
import time
from clockdeco_param import clock @clock('{name}({args}) dt={elapsed:0.3f}s')
def snooze(seconds):
time.sleep(seconds) for i in range(3):
snooze(.123)
示例 7-27 的输出:
$ python3 clockdeco_param_demo2.py
snooze(0.123) dt=0.124s
snooze(0.123) dt=0.124s
snooze(0.123) dt=0.124s
python 装饰器(四):装饰器基础(三)叠放装饰器,参数化装饰器的更多相关文章
- python函数调用的四种方式 --基础重点
第一种:参数按顺序从第一个参数往后排#标准调用 # -*- coding: UTF-8 -*- def normal_invoke(x, y): print "--normal_invoke ...
- Python之路第四天,基础(4)-装饰器,迭代器,生成器
装饰器 装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法或者类进行加工.在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象 ...
- python基础-第五篇-5.3装饰器
小白发呆的看着窗外,同事们陆陆续续的地来到公司,想起算法,小白就飘飘然了.突然后面传来一声呼唤,原来是小刘! 小刘:不好意思啊!堵车了,就来晚了点,不耽误你的时间,咱们就开启的今天的培训内容吧! 小白 ...
- python 基础学习笔记(8)--装饰器
**装饰器** - [ ] 装饰器和闭包有很大的联系.有时你需要在不改变源代码的情况下修改已经存在的函数.装饰器的运用可以提高效率,减少重复的代码. - [ ] 装饰器的实质是一个函数.它把一个函数作 ...
- python基础学习之描述符和装饰器
描述符的了解: 描述符协议: python描述符是一个"绑定行为"的对象属性,在描述符协议中,它可以通过方法重写属性的访问.这些方法有: __get__, __set__, 和__ ...
- python基础语法7 闭包函数与装饰器
闭包函数: 1.闭包函数必须在函数内部定义 2.闭包函数可以引用外层函数的名字 闭包函数是 函数嵌套.函数对象.名称空间与作用域 结合体. # 直接传参 def func(x): print(x) f ...
- Python基础之函数的闭包与装饰器的介绍
1.闭包的概念: 如果在一个函数中,定义了另外一个函数,并且那个函数使用了外面函数的变量,并且外面那个函数返回了里面这个函数的引用,那么称为里面的这个函数为闭包. 2.话不多说,以demo示例: de ...
- python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解
1.前言 Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题 ...
- Python之路【第五篇】: 函数、闭包、装饰器、迭代器、生成器
目录 函数补充进阶 函数对象 函数的嵌套 名称空间与作用域 闭包函数 函数之装饰器 函数之可迭代对象 函数之迭代器 函数之生成器 面向过程的程序设计思想 一.函数进阶之函数对象 1. 函数对象 秉承着 ...
随机推荐
- 网络聚合Network Teaming
team是新的聚合软件,依赖于安装包teamd,可以通过nmcli管理. team和bond的区别在于,支持hash加密,支持负载均衡,支持8块网卡,更好地支持IPV6,总之要取代bond. 1. 添 ...
- 最后一面挂在volatile关键字上,面试官:重新学学Java吧!
最后一面挂在volatile关键字上,面试官:重新学学Java吧! 为什么会有volatile关键字? volatile: 易变的; 无定性的; 无常性的; 可能急剧波动的; 不稳定的; 易恶化的; ...
- NativeXml实例训练时注意事项_1
NativeXml实例训练: 1)使用NativeXml操作xml文件时,需要将几个单元文件在Library中引用,配置好这个后面的就可自由训练.或按照自己想要的组合折腾. 2)运行程序调 ...
- android中getWidth()和getMeasuredWidth()之间的区别
先给出一个结论:getMeasuredWidth()获取的是view原始的大小,也就是这个view在XML文件中配置或者是代码中设置的大小.getWidth()获取的是这个view最终显示的大小,这个 ...
- 商城02——dubbo框架整合_商品列表查询实现_分页
1. 课程计划 1.服务中间件dubbo 2.SSM框架整合. 3.测试使用dubbo 4.后台系统商品列表查询功能实现. 5.监控中心的搭建 2. 功能分析 2.1. 后台系统所用的技术 框 ...
- ca71a_c++_指向函数的指针_通过指针调用函数txwtech
/*ca71a_c++_指向函数的指针_通过指针调用函数用typedef简化函数指针的定义简化前: bool(*pf)(const string&, const string &); ...
- Fabric网络组织与主节点选举
一.Fabric网络组织 Fabric网络组织按如下结构组成:Fabric网络-->Channel通道-->组织(成员)-->节点.即整个网络由数个通道组成,每个通道都由多个组织构成 ...
- fork,vfork和clone底层实现
分类: LINUX2011-10-13 09:33 1116人阅读 评论(0) 收藏 举报 structdstsignalthreadnulldomain fork,vfork,clone都是linu ...
- jwt 工具类
public class TokenUtils { private Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 签名 ...
- 【Flutter实战】定位装饰权重组件及柱状图案例
老孟导读:Flutter中有这么一类组件,用于定位.装饰.控制子组件,比如 Container (定位.装饰).Expanded (扩展).SizedBox (固定尺寸).AspectRatio (宽 ...