zoukankan      html  css  js  c++  java
  • Python 3----装饰器

    转自:https://zhuanlan.zhihu.com/p/21696291

    平时用Python的方式都是短平快,很少用到装饰器特性,而Python装饰器的实现也是有一定“套路”的,恰好最近在看「Python语言及其应用」这本书,看到里面的装饰器章节(4.9 装饰器的内容),觉得有必要进一步描述,以强化一下概念。

    一. 预备知识

    首先你得理解4.7.9 闭包的概念。举该章节的例子:

    def knights2(saying):
        def inner2():
            return "We are the knights who say: '%s'" % saying
        return inner2

    这里的关键是return inner2,其返回一个函数inner2(不是该函数执行结果,下面提到的inner2都是指return inner2中的inner2),原书说inner2是闭包。但更确切地讲,由于inner2引用了外层变量saying,闭包应该是inner2加上绑定了saying的作用域。这样,return inner2之后,saying才不会消失。当然,闭包的具体表现是通过inner2体现出来的:

    a = knights2('Duck')
    b = knights2('Hasenpfeffer')
    
    print(a(), '---', a)
    print(b(), '---', b)

    输出:

    We are the knights who say: 'Duck' --- <function knights2.<locals>.inner2 at 0x0000026479031510>
    We are the knights who say: 'Hasenpfeffer' --- <function knights2.<locals>.inner2 at 0x0000026479031598>

    二. 装饰器

    装饰器的定义是:装饰器实质上是一个函数。它把一个函数作为输入并且返回另外一个函数。其实其是闭包概念的深化。依然是本书例子:

    def document_it(func):
        def new_function(*args, **kwargs):
            print('Running function:', func.__name__)
            print('Positional arguments:', args)
            print('Keyword arguments:', kwargs)
            result = func(*args, **kwargs)
            print('document_it Result:', result)
            return result
        return new_function

    这里定义了一个装饰器,往函数document_it传入一个函数func,然后在document_it里定义一个new_function函数,该函数一方面有其自己的逻辑,另一方面也撩了撩func:result = func(*args, **kwargs),请记住,这里是确实调用了func。最后,将整个new_function返回给外部了,这个new_function从模糊概念上来讲,是个闭包,如“预备知识”里讲的,它不是该函数执行结果。

    接下来,定义即将要应用装饰器函数的函数add_ints:

    def add_ints(a, b):
        return a + b

    手工应用装饰器函数方式

    先来看看如何手工应用装饰器函数:

    >>> cooler_add_ints = document_it(add_ints)
    >>> cooler_add_ints(3, 5)
    Running function: add_ints
    Postitional arguments:(3, 5)
    Keyword arguments: {}
    document_it Result: 8
    8

    这里不难理解:

    • cooler_add_ints = document_it(add_ints)中,document_it(add_ints)调用返回了一个持有add_ints的闭包函数new_function给cooler_add_ints引用;
    • cooler_add_ints(3, 5)调用时,参数3跟5传给了new_function函数(实际上cooler_add_ints(3, 5)可以看成new_function(3, 5))。然后,new_function再在自己内部用参数3跟5调用第一步传进的func——即add_ints,生成结果。

    理解以上步骤很重要,因为下面阐述的内容,其机制是一致的。

    使用装饰器名字来装饰函数

    相对于前面的手工应用装饰器函数方式,Python提供的装饰器名字方式,则让Python代码显得更为高大上一些:

    @document_it
    def add_ints(a, b):
        return a + b

    运行结果如下:

    >>> add_ints(3, 5)
    Start function add_ints
    Positional arguments: (3, 5)
    Keyword arguments: {}
    document_it Result: 8
    8

    这里的机理跟前面的手工应用装饰器函数方式一样,就语法糖而已,在此不再多说。

    应用多个装饰器

    可以为函数应用多个装饰器,如本书提及:“靠近装饰目标函数定义(def上面)的装饰器最先执行,然后依次执行上面的”。这里的执行,还请理解成“生成闭包的过程”

    再定义一个本书中新的装饰器函数:

    def square_it(func):
        def another_new_function(*args, **kwargs):
            result = func(*args, **kwargs)
            return result * result
        return another_new_function

    其实,理解了“手工应用装饰器函数方式”中的两个步骤,再理解这里的内容就不是什么难事了,无非就是多套了一层膜。

    先来看看第一种情况:

    >>> @document_it
    ... @square_it
    ... def add_ints(a, b):
    ... return a + b
    ...
    >>> add_ints(3, 5)
    Running function: another_new_function
    Positional arguments: (3, 5)
    Keyword arguments: {}
    document_it Result: 64
    64

    来分析一下。首先应用的是@square_it,此时返回的闭包其实是another_new_function。之后才是将another_new_function应用给@document_it,返回new_function闭包。整个过程用以下图示来说明就一目了然了:

    特别说明:这里原作者的意思是下面的代码解释,可是在1处作者把这个装饰器的先后顺序搞反了,但是下面的解释都是正确的,只是这个图有点问题

    现在反过来调用:

    >>> @square_it
    ... @document_it
    ... def add_ints(a, b):
    ... return a + b
    ...
    >>> add_ints(3, 5)
    Running function: add_ints
    Positional arguments: (3, 5)
    Keyword arguments: {}
    document_it Result: 8
    64

    理解起来的思路跟上一种情况一样:

    三. 小结

    仔细想想,装饰器还是蛮有意义的,只要想在方法执行前后添加一些行为,都有可能用到它,比如拦截器、路由设置、调试增强等等任务。希望后续自己也多留意一些使用场景。

  • 相关阅读:
    最近在学习vala,并打算用它写点东西, 先上个习作 -- popplersample
    GTK# 学习笔记
    monodevelop2.0在SmartQ5上成功运行
    c, cs, vala 性能简单测试
    在Q5上用vala写程序
    树结构型数据在基于关系型数据库的分布式应用的解决方案
    常用常忘的delegate,记一下。
    JSON
    HTML5脚本编程
    event事件对象
  • 原文地址:https://www.cnblogs.com/loved/p/8694935.html
Copyright © 2011-2022 走看看