zoukankan      html  css  js  c++  java
  • Python装饰器(Decorator)简介

    Python有许多出色的语言特性,装饰器(Decorator)便是其中一朵奇葩。先来看看一段代码:

    def deco1(f):
        print 'decorate 1'
        return f
    
    def deco2(f):
        print 'decorate 2'
        return f
    
    @deco1
    @deco2
    def foo(): return 'hello'

    保存并执行上面的代码,你会看到如下输出:

    decorate 2
    decorate 1

    函数foo没有被调用,但是deco1,deco2被按照一个顺序被调用了。deco1和deco2就是装饰器。从上面的输出可以看出两点:

    1 装饰器是在代码装载时被调用的

    2 调用的顺是从下到上

    现在来说说装配器到底做了什么。看代码:

    def deco1(f):
        print 'decorate 1',f
        return f
    
    def deco2(f):
        print 'decorate 2',f
        return f
    
    @deco1
    @deco2
    def foo():
        return 'hello'

    这段代码的执行结果为:

    decorate 2 <function foo at 0x00000000021F79E8>
    decorate 1 <function foo at 0x00000000021F79E8>

    装饰器函数的参数‘f’被打印出来,而且就是我们的函数‘foo’,可见装饰器的参数就是被装饰的函数。真的是这样吗?再来看代码:

    def wraper(n,f):
        def foo1():
            return '%s(%s)'%(n,f())return foo1
        
    
    def deco1(f):
        print 'decorate 1',f
        return wraper('decorate 1',f)
    
    def deco2(f):
        print 'decorate 2',f
        return wraper('decorate 2',f)
    
    @deco1
    @deco2
    def foo():
        return 'hello'

    这段代码的执行结果为:

    decorate 2 <function foo at 0x0000000002147A58>
    decorate 1 <function foo1 at 0x0000000002147AC8>

    我觉得我现在可以总结一下了:

    1 装饰器在代码装载时被调用;

    2 调用顺序是从下到上的;

    3 被装饰函数‘foo’作为参数传递给第一个装饰器‘deco2’,返回值将作为参数传递给第二个装饰器‘deco1’,然后依次向上直到最顶端的装饰器;

    4 最顶端的装饰器的返回值就是被装饰以后的函数,我们暂时称之为,也就是我们将来要执行的那个‘foo’。

     下面,来我们说说装饰后的函数,被调用会有什么结果,看代码:

    def wraper(n,f):
        def foo1():
            return '%s(%s)'%(n,f())
        return foo1
    
    def deco1(f):
        print 'decorate 1',f
        return wraper('decorate 1',f)
    
    def deco2(f):
        print 'decorate 2',f
        return wraper('decorate 2',f)
    
    @deco1
    @deco2
    def foo():
        return 'hello'
    
    
    print foo()

    这段代码的执行结果为:

    decorate 2 <function foo at 0x0000000002127A58>
    decorate 1 <function foo1 at 0x0000000002127AC8>
    decorate 1(decorate 2(hello))

    不难看出,调用顺序与装饰顺序刚好相反,最终函数也就是最顶端的装饰器返回的函数最先被调用,然后依次调用到最初那个函数‘foo’。我在这里故意规避了一个基本的事实,就是:其实最后被调用的就是顶端装饰返回的那个函数,你必须手动在这个函数中调用前面的函数,见这里:

    return '%s(%s)'%(n,f())

    ,才会产生调用连的效果。所以装饰器并不会帮你完成所有的事情,他给了你充分的自由。说的这里,你们要问了(如果你有足够的好奇心):‘装饰器能带参数吗’。答案是能,见下面的代码:

    def wraper(n,f):
        def foo1():
            return '%s(%s)'%(n,f())
        return foo1
    
    def deco(n):
        def deco1(f):
            print n,f
            return wraper(n,f)
        return deco1
    
    @deco('decorate 1')
    @deco('decorate 2')
    def foo():
        return 'hello'
    
    
    print foo()

    这段代码的执行结果为:

    decorate 2 <function foo at 0x0000000002117AC8>
    decorate 1 <function foo1 at 0x0000000002117B38>
    decorate 1(decorate 2(hello))

    哇,和之前的结果一模一样,代码还被简化了不少。这是怎么回事呢,我来简单解释下,函数‘deco’不是装饰器,他只是一个返回装饰器的函数,当你把它放到装饰符号‘@‘后面时,python的语法起了一个美妙的作用,他会先调用这个函数,然后用返回值值作为装饰器。

    大概就是这样啦,语法大师也许会描述的更详细更专业。源码狂人还可能深入到python的C代码里寻找成因。不过,作为一个有理智的好青年我们就点到为止吧。

    下次,我们再来掰扯一下,装饰器都可以变出哪些戏法吧。洗洗睡了。

  • 相关阅读:
    redis系列之------过期策略
    总结与期盼
    服务不可用排查思路
    Spring Boot Starters到底怎么回事?
    redis系列之------主从复制
    redis系列之------对象
    redis系列之------数据库
    DirectX11 With Windows SDK--00 目录
    DirectX11 With Windows SDK--34 位移贴图
    DirectX11 With Windows SDK--33 曲面细分阶段(Tessellation)
  • 原文地址:https://www.cnblogs.com/sillyemperor/p/4442590.html
Copyright © 2011-2022 走看看