zoukankan      html  css  js  c++  java
  • 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_levelset_message就是访问器函数,这个访问器函数被attach_wrapper所装饰。我们通过断点调试来看下每步的执行过程:

    首先执行add函数的装饰功能

    @logged(logging.DEBUG)

    def add(x,y):

    return x+y

    此时进入logged函数

    在第一次运行set_level的时候,此时传入装饰器的参数objadd函数,funcNone

    此时由于funcNone, 执行partial函数,partial是中偏函数,函数在执行时,要带上所有必要的参数进行调用。但是,有时参数可以在函数被调用之前提前获知。这种情况下,一个函数有一个或多个参数预先就能用上,以便函数能用更少的参数进行调用。

    例如下面的例子:

    In [12]: plus = partial(add,100)

    In [13]: plus(9)
    Out[13]: 109

    这里讲下attach_wrapper的用法:

    首次执行的时候是attach_wrapper(wrapper)(set_level)。此时wrapperadd函数。

    经过执行partial后,变成了attach_wrapper(wrapper,set_level)

    最后通过调用setattr(obj,func.__name__,func)执行属性,此时obj=addfunc.__name__set_levelfuncset_level函数。执行完成后add函数就有了一个属性set_level。也就是add.set_level=set_level(). 至次,我们就通过装饰器的方法将set_level函数添加到了add的属性里面。同理set_message

    关于这种功能的使用有2点需要注意:

    set_levelset_message函数中使用了nonlocal关键字,使用nonlocal关键字,那么使用的level参数就是在logged中使用的level参数。

    对于这种装饰器的使用方法,必须使用@wraps(func)的方法。这样才能保持被装饰函数的独立性。否则addspam就被成了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

  • 相关阅读:
    多种开源OLAP引擎测评报告
    Go的单元测试
    C#的List实现IComparer接口排序实例
    Java 多线程:(一)
    android:theme
    android:excludeFromRecents="true"
    RK:主屏幕
    Gatsby xinhua log boork(三)
    RK:Provision.apk、SettingsProvider的分析、使用
    Camera(一):查看Camera设备详细信息
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/9573995.html
Copyright © 2011-2022 走看看