美文网首页
Python装饰器

Python装饰器

作者: Yankee_13 | 来源:发表于2018-12-25 16:57 被阅读0次

    献给莹莹

    1.什么是装饰器

    以下为装饰器例子

    def w1(func):
        def inner():
            print('...验证权限...')
            func()
        return inner
    
    @w1
    def f1():
        print('f1 called')
    
    @w1
    def f2():
        print('f2 called')
    
    f1()
    f2()
    

    该程序的输出为:

    ...验证权限...
    f1 called
    ...验证权限...
    f2 called
    
    • 首先,来看我们的装饰器函数w1,该函数接收一个参数func,其实就是接收一个方法名,w1内部又定义一个函数inner,在inner函数中增加权限校验,并在验证完权限后调用传进来的参数func,同时w1的返回值为内部函数inner,其实就是一个闭包函数。
    • 然后,再来看一下,在f1上增加@w1,那这是什么意思呢?当python解释器执行到这句话的时候,会去调用w1函数,同时将被装饰的函数名作为参数传入(此时为f1),根据闭包一文分析,在执行w1函数的时候,此时直接把inner函数返回了,同时把它赋值给f1,此时的f1已经不是未加装饰时的f1了,而是指向了w1.inner函数地址。
    • 接下来,在调用f1()的时候,其实调用的是w1.inner函数,那么此时就会先执行权限验证,然后再调用原来的f1(),该处的f1就是通过装饰传进来的参数f1。
    • 这样下来,就完成了对f1的装饰,实现了权限验证。

    实际上python执行到@w1时,就已经开始装饰,相当于执行了:

    f1=w1(f1)
    

    2.两个装饰器的执行流程和装饰结果

    def makeBold(fun):
        print('----a----')
        def inner():
            print('----1----')
            return '<b>' + fun() + '</b>'
        return inner
    
    def makeItalic(fun):
        print('----b----')
        def inner():
            print('----2----')
            return '<i>' + fun() + '</i>'
        return inner
    
    @makeBold
    @makeItalic
    def test():
        print('----c----')
        print('----3----')
        return 'hello python decorator'
    
    ret = test()
    print(ret)
    

    输出结果:

    ----b----
    ----a----
    ----1----
    ----2----
    ----c----
    ----3----
    <b><i>hello python decorator</i></b>
    
    • 以上程序较为复杂,可以这样理解
    • 1.装饰时机 通过上面装饰时机的介绍,我们可以知道,在执行到@makeBold的时候,需要对下面的函数进行装饰,此时解释器继续往下走,发现并不是一个函数名,而又是一个装饰器,这时候,@makeBold装饰器暂停执行,而接着执行接下来的装饰器@makeItalic,接着把test函数名传入装饰器函数,在makeItalic装饰完后,此时的test指向makeItalic的inner函数地址,这时候有返回来执行@makeBold,接着把新test传入makeBold装饰器函数中。
    @makeItalic 等价于 test=makeItalic(test) 
    @makeBold 等价于 test=makeBold(makeItalic.inner)
    

    3.通用装饰器----对无参函数和有参函数进行装饰的区别

    • 上面例子中都是对无参函数的装饰,不再单独举例
    • 对有参数函数装饰
    def w_say(fun):
    
        def inner(name):
            print('say inner called')
            fun(name)
    
        return inner
    
    @w_say
    def hello(name):
        print('hello ' + name)
    
    

    只需要修改inner即可。
    对于多个或不定长参数时,可以参考如下:

    def w_add(func):
        def inner(*args, **kwargs):
            print('add inner called')
            func(*args, **kwargs)
        return inner
    
    @w_add
    def add(a, b):
        print('%d + %d = %d' % (a, b, a + b))
    
    @w_add
    def add2(a, b, c):
        print('%d + %d + %d = %d' % (a, b, c, a + b + c))
    

    如果函数有返回值,在inner内返回即可

    def w_test(func):
        def inner():
            print('w_test inner called start')
            str = func()
            print('w_test inner called end')
            return str
        return inner
    
    @w_test
    def test():
        print('this is test fun')
        return 'hello'
    

    如果装饰器本身需要参数,可以这样写:

    def func_args(pre=None):
        def w_test_log(func):
            def inner():
                print('...记录日志...visitor is %s' % pre)
                func()
            return inner
        return w_test_log
    
    @func_args('yingying')
    def test_log():
        print('this is test log')
    

    即在外嵌套一层函数
    至此,我们就可以引出通用的装饰器写法了

    def w1(pre=None):
        def w1_test(func):
            def inner(*args,**kwargs):
                print("{} is now using the system".format(pre))
                ret=func(*args,**kwargs)
                return ret
            return inner
        return w1_test
    
    @w1("icey")
    def test_log(a,b,c,d=True):
        res = a + b
        if d:
            print(a+b+c)
        return res
    

    以上程序输出结果

    icey is now using the system
    9
    

    4.类装饰器

    结合类的特性来说,类可以记录数据(属性),而函数不行(闭包某种意义上也可行),利用这种特性可以实现基于类的装饰器

    1

    class Test(object):
        def __init__(self, func):
            print('test init')
            print('func name is %s ' % func.__name__)
            self.__func = func
    
        def __call__(self, *args, **kwargs):
            print('装饰器中的功能')
            return self.__func()
    
    @Test
    def test():
        print('this is test func')
    
    test()
    

    输出结果为:

    test init
    func name is test 
    装饰器中的功能
    this is test func
    

    2

    class Counter:
        def __init__(self, func):
            self.func = func
            self.count = 0
    
        def __call__(self, *args, **kwargs):
            self.count += 1
            return self.func(*args, **kwargs)
    
    @Counter
    def foo():
        pass
    
    for i in range(10):
        foo()
    
    print(foo.count)
    

    实际上相当于执行了foo=Counter(foo()),并执行了__call__内的内容。

    参考资料:https://blog.csdn.net/u010358168/article/details/77773199

    相关文章

      网友评论

          本文标题:Python装饰器

          本文链接:https://www.haomeiwen.com/subject/eggflqtx.html