python cookbook第三版学习笔记十八:可由用户修改的装饰器
定义一个属性可由用户修改的装饰器:
在前面的介绍中使用装饰器来包装函数,这一章来介绍下如何让用户调整装饰器的属性。
首先来看下代码:
from functools import wraps,partial
import logging
def attach_wrapper(obj,func=None):
if func is None:
return partial(attach_wrapper,obj)
print(obj)
setattr(obj,func.__name__,func)
def logged(level,name=None,message=None):
def decorate(func):
logname=name if name else func.__module__
log=logging.getLogger(logname)
logmsg=message if message else func.__name__
@wraps(func)
def wrapper(*args,**kwargs):
log.log(level,logmsg)
return func(*args,**kwargs)
@attach_wrapper(wrapper)
def set_level(newlevel): #访问器函数
nonlocal level
level=newlevel
@attach_wrapper(wrapper)
def set_message(newmsg): #访问器函数
nonlocal logmsg
logmsg=newmsg
return wrapper
return decorate
@logged(logging.DEBUG)
def add(x,y):
return x+y
@logged(logging.CRITICAL,'example')
def spam():
print('Spam!')
在上面的代码中set_level和set_message就是访问器函数,这个访问器函数被attach_wrapper所装饰。我们通过断点调试来看下每步的执行过程:
首先执行add函数的装饰功能
@logged(logging.DEBUG)
def add(x,y):
return x+y
此时进入logged函数
在第一次运行set_level的时候,此时传入装饰器的参数obj为add函数,func为None

此时由于func为None, 执行partial函数,partial是中偏函数,函数在执行时,要带上所有必要的参数进行调用。但是,有时参数可以在函数被调用之前提前获知。这种情况下,一个函数有一个或多个参数预先就能用上,以便函数能用更少的参数进行调用。
例如下面的例子:
In [12]: plus = partial(add,100)
In [13]: plus(9)
Out[13]: 109

这里讲下attach_wrapper的用法:
1 首次执行的时候是attach_wrapper(wrapper)(set_level)。此时wrapper是add函数。
2 经过执行partial后,变成了attach_wrapper(wrapper,set_level)
最后通过调用setattr(obj,func.__name__,func)执行属性,此时obj=add,func.__name__为set_level,func为set_level函数。执行完成后add函数就有了一个属性set_level。也就是add.set_level=set_level(). 至次,我们就通过装饰器的方法将set_level函数添加到了add的属性里面。同理set_message。
关于这种功能的使用有2点需要注意:
1 在set_level和set_message函数中使用了nonlocal关键字,使用nonlocal关键字,那么使用的level参数就是在logged中使用的level参数。
2 对于这种装饰器的使用方法,必须使用@wraps(func)的方法。这样才能保持被装饰函数的独立性。否则add和spam就被成了wrapper函数。
我们在attach_wrapper添加打印来看下实际的调用结果
def attach_wrapper(obj,func=None):
print("attach_wrapper:"+str(func))
if func is None:
return partial(attach_wrapper,obj)
print(obj)
setattr(obj,func.__name__,func)
return func
调用结果:
attach_wrapper:None
attach_wrapper:<function logged.<locals>.decorate.<locals>.set_level at 0x7f2186a94730>
<function add at 0x7f2186a946a8>
attach_wrapper:None
attach_wrapper:<function logged.<locals>.decorate.<locals>.set_message at 0x7f2186a947b8>
<function add at 0x7f2186a946a8>
attach_wrapper:None
attach_wrapper:<function logged.<locals>.decorate.<locals>.set_level at 0x7f2186a94950>
<function spam at 0x7f2186a948c8>
attach_wrapper:None
attach_wrapper:<function logged.<locals>.decorate.<locals>.set_message at 0x7f2186a949d8>
<function spam at 0x7f2186a948c8>
最后来看下最终的调用:
if __name__=="__main__":
logging.basicConfig(level=logging.DEBUG)
add.set_level(logging.WARNING)
print(add(2, 3))
add.set_message("Add called")
print(add(3,4))
运行结果:
5
WARNING:__main__:add
7
WARNING:__main__:Add called
python cookbook第三版学习笔记十八:可由用户修改的装饰器的更多相关文章
- python cookbook第三版学习笔记十:类和对象(一)
类和对象: 我们经常会对打印一个对象来得到对象的某些信息. class pair: def __init__(self,x,y): self.x=x self. ...
- python cookbook第三版学习笔记十六:抽象基类
假设一个工程中有多个类,每个类都通过__init__来初始化参数.但是可能有很多高度重复且样式相同的__init__.为了减少代码.我们可以将初始化数据结构的步骤归纳到一个单独的__init__函数中 ...
- python cookbook第三版学习笔记十五:property和描述
8.5 私有属性: 在python中,如果想将私有数据封装到类的实例上,有两种方法:1 单下划线.2 双下划线 1 单下划线一般认为是内部实现,但是如果想从外部访问的话也是可以的 2 双下划线是则无法 ...
- python cookbook第三版学习笔记十二:类和对象(三)创建新的类或实例属性
先介绍几个类中的应用__getattr__,__setattr__,__get__,__set__,__getattribute__,. __getattr__:当在类中找不到attribute的时候 ...
- python cookbook第三版学习笔记十四:类和对象(五)代理类以及内存回收
代理类: 代理类的作用其实有继承有些类似,如果你想将某个实例的属性访问代理到内部另外一个实例中去,可以用继承也可以用代理.来看下代理的应用: class A: def spam(self,x) ...
- python cookbook第三版学习笔记十九:未包装的函数添加参数
比如有下面如下的代码,每个函数都需要判断debug的是否为True,而默认的debug为False def a(x,debug=False): if debug: print('calling a') ...
- python cookbook第三版学习笔记二十:可自定义属性的装饰器
在开始本节之前,首先介绍下偏函数partial.首先借助help来看下partial的定义 首先来说下第一行解释的意思: partial 一共有三个部分: (1)第一部分也就是第一个参数,是一个函数, ...
- python cookbook第三版学习笔记二十一:利用装饰器强制函数上的类型检查
在演示实际代码前,先说明我们的目标:能对函数参数类型进行断言,类似下面这样: @typeassert(int, int) ... def add(x, y): ... return x + y ...
- python cookbook第三版学习笔记九:函数
接受任意数量参数的函数. 当传入函数的参数个数很多的时候,在函数定义的时候不需要为每一个参数定义一个变量,可以用*rest的方式来包含多余的参数. 如下面的代码,*rest包含了2,3,4这3个参数. ...
随机推荐
- 《深入理解Android 卷III》第七章 深入理解SystemUI
<深入理解Android 卷III>即将公布,作者是张大伟.此书填补了深入理解Android Framework卷中的一个主要空白,即Android Framework中和UI相关的部分. ...
- linux系统预留内存和磁盘大小
默认情况下, Linux 会最多使用 40% 的可用内存作为文件系统缓存.当超过这个阈值后,文件系统会把将缓存中的内存全部写入磁盘, 导致后续的 IO 请求都是同步的. 将缓存写入磁盘时,有一个默认1 ...
- jmap命令(Java Memory Map)的使用
jmap的使用能够參考: 官方文档 http://docs.oracle.com/javase/6/docs/technotes/tools/share/jmap.html 和这篇博客 http:// ...
- linux在shell中获取时间 date巧用
获得当天的日期 date +%Y-%m-%d 输出: 2011-07-28 date1=$(date --date='1 days ago +%Y%m%d') #前一天的日期 date1=$(d ...
- SQL的四种连接
SQL的四种连接-内连接.左外连接.右外连接.全连接 今天在看一个遗留系统的数据表的时候发现平时查找的视图是FULL OUT JOIN的,导致平时的数据记录要进行一些限制性处理,其实也可以设置视图 ...
- kvo&kvc
Key Value Coding Key Value Coding是cocoa的一个标准组成部分,它能让我们可以通过name(key)的方式访问property, 不必调用明确的property ac ...
- Spring事务管理之声明式事务管理-基于注解的方式
© 版权声明:本文为博主原创文章,转载请注明出处 案例 - 利用Spring的声明式事务(TransactionProxyFactoryBean)管理模拟转账过程 数据库准备 -- 创建表 CREAT ...
- spring源码解析之IOC容器(二)------加载和注册
上一篇跟踪了IOC容器对配置文件的定位,现在我们继续跟踪代码,看看IOC容器是怎么加载和注册配置文件中的信息的.开始之前,首先我们先来了解一下IOC容器所使用的数据结构-------BeanDefin ...
- mac eclipse 删除不用的workspace
file--->switch workspace---->other 点击 recent workspace--->选中删除即可
- inline用于替代宏函数
在C&C++中 一.inline关键字用来定义一个类的内联函数,引入它的主要原因是用它替代C中表达式形式的宏定义. 表达式形式的宏定义一例: #define ExpressionName(Va ...