定义一个属性可由用户修改的装饰器:

在前面的介绍中使用装饰器来包装函数,这一章来介绍下如何让用户调整装饰器的属性。

首先来看下代码:

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第三版学习笔记十八:可由用户修改的装饰器的更多相关文章

  1. python cookbook第三版学习笔记十:类和对象(一)

    类和对象: 我们经常会对打印一个对象来得到对象的某些信息. class pair:     def __init__(self,x,y):         self.x=x         self. ...

  2. python cookbook第三版学习笔记十六:抽象基类

    假设一个工程中有多个类,每个类都通过__init__来初始化参数.但是可能有很多高度重复且样式相同的__init__.为了减少代码.我们可以将初始化数据结构的步骤归纳到一个单独的__init__函数中 ...

  3. python cookbook第三版学习笔记十五:property和描述

    8.5 私有属性: 在python中,如果想将私有数据封装到类的实例上,有两种方法:1 单下划线.2 双下划线 1 单下划线一般认为是内部实现,但是如果想从外部访问的话也是可以的 2 双下划线是则无法 ...

  4. python cookbook第三版学习笔记十二:类和对象(三)创建新的类或实例属性

    先介绍几个类中的应用__getattr__,__setattr__,__get__,__set__,__getattribute__,. __getattr__:当在类中找不到attribute的时候 ...

  5. python cookbook第三版学习笔记十四:类和对象(五)代理类以及内存回收

    代理类: 代理类的作用其实有继承有些类似,如果你想将某个实例的属性访问代理到内部另外一个实例中去,可以用继承也可以用代理.来看下代理的应用: class A:     def spam(self,x) ...

  6. python cookbook第三版学习笔记十九:未包装的函数添加参数

    比如有下面如下的代码,每个函数都需要判断debug的是否为True,而默认的debug为False def a(x,debug=False): if debug: print('calling a') ...

  7. python cookbook第三版学习笔记二十:可自定义属性的装饰器

    在开始本节之前,首先介绍下偏函数partial.首先借助help来看下partial的定义 首先来说下第一行解释的意思: partial 一共有三个部分: (1)第一部分也就是第一个参数,是一个函数, ...

  8. python cookbook第三版学习笔记二十一:利用装饰器强制函数上的类型检查

    在演示实际代码前,先说明我们的目标:能对函数参数类型进行断言,类似下面这样: @typeassert(int, int) ... def add(x, y): ...     return x + y ...

  9. python cookbook第三版学习笔记九:函数

    接受任意数量参数的函数. 当传入函数的参数个数很多的时候,在函数定义的时候不需要为每一个参数定义一个变量,可以用*rest的方式来包含多余的参数. 如下面的代码,*rest包含了2,3,4这3个参数. ...

随机推荐

  1. 系统封装 ES3使用方法

    1 什么是系统封装? 系统封装,说简单就是把系统制作成镜像的方法制作Ghost镜像文件,用在系统安装上面.系统封装,不同于系统的正常安装.最本质的区别在于 系统封装 是将一个完整的系统以拷贝的形式打包 ...

  2. vue笔记二

    七.列表渲染 1.示例 <ul id="example-2"> <li v-for="(item, index) in items"> ...

  3. java中native方法的使用

    在非常多情况下,java须要调用其它语言的代码,比方c的代码.那么这个时候java中native方法就发挥作用了.以下就介绍native方法的使用. 一.JNI使用流程 a.编写带有native声明的 ...

  4. 2.JAVA编程思想——一切都是对象

    一切都是对象 欢迎转载.转载请标明出处:http://blog.csdn.net/notbaron/article/details/51040221 虽然以C++为基础,但 Java 是一种更纯粹的面 ...

  5. 【0】按照Django官网:实现第一个django app 安装必要的工具/模块

    1.环境配置: (1)Install Setuptools¶ To install Python packages on your computer, Setuptools is needed. Do ...

  6. Android採用async框架实现文件上传

    页面效果 须要的权限 <uses-permission android:name="android.permission.INTERNET"/> 网络訪问权限; 布局文 ...

  7. 题目1 : Farthest Point

    时间限制:5000ms 单点时限:1000ms 内存限制:256MB 描述 Given a circle on a two-dimentional plane. Output the integral ...

  8. 微信支付v3开发(6) 收货地址共享接口

    请看新版教程  微信支付开发(7) 收货地址共享接口V2 本文介绍微信支付下的收货地址共享接口的开发过程. 一. 简单介绍 微信收货地址共享,是指用户在微信浏览器内打开网页,填写过地址后,兴许能够免填 ...

  9. struts2中配置文件加载的顺序是什么?

    struts2的StrutsPrepareAndExecuteFilter拦截器中对Dispatcher进行了初始化 在Dispatcher类的init方法中定义了配置文件的加载顺序(下面是源码) p ...

  10. 【Python学习】之yagmail库实现发送邮件

    上代码: import yagmail sendmail = 'xxx@126.com' sendpswd = 'xxx' receivemail = 'xxx@qq.com' # 连接邮箱服务器 y ...