zoukankan      html  css  js  c++  java
  • python__高级 : @修饰器(装饰器)的理解

    以下是第一次了解的时候写的东西,有的地方理解不正确,虽已改正但是太片面,请直接看下面第二次修改加上的内容.

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能.

    装饰器的作用就是为已经存在的对象添加额外的功能。

    def funA(fun):
        print (fun())
    
    def funB():
        print ('B')
        return 1
    
    funA(funB)
    -----------------------------
    >>>
    B
    1

    可以看出,这个程序的运行过程为:

    1.执行函数funA,把funB当作参数传进去, print(fun()) 这一句 执行了 funB, 然后打印 'B' , 返回 1  

    2. print(fun()) 这一句把 返回的 1 打印出来

    而修饰器的作用:

    def funA(fun):  #函数本身也是对象,所以可以将函数作为参数传入另一函数并进行调用,而funB是有返回值的,所以结果输出了返回值1.(个人理解)
        print (fun())
    
    @funA
    def funB():
        print ('B')
        return 1
    ----------------------
    >>>
    B
    1

    作用相当于 funB = funA(funB),不过只能放在一个函数或者类定义之前   

    需要注意的是,如果funB在funA里没用被调用的话,那funB是不会被执行的,如:

    def funA(fun):
        print ('funA')
    
    @funA
    def funB():
        print ('B')
        return 1
    ---------------------------
    >>>
    funA

    可以看出,只执行了funA而funB没有被执行,因为print('B')并没有被打印出来.

    ps:如果funA不加参数的话,比如直接 def funA(): 这样定义,他是会报错的:

    @funA
    TypeError: funA() takes 0 positional arguments but 1 was given

    大意是@funA中的funA必须要给他提供一个参数,但是你给了0个.

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    第二次修改:

    第一次了解的是一些浅层次的东西,把它深入一下,看个例子:

    def w1(func):
        print('正在装饰')
        def cou():
            print('22222222222')
            func()
        return cou
    
    @w1
    def f1():
        print('11111111111')
    
    >>>正在装饰

    可以看出,装饰器 @w1 这一行,其实在函数没有被调用之前已经执行了, 这一句就等于  f1=w1(f1)  所以 w1 函数已经被调用了,返回的是 cou函数的引用,

    所以说如果再调用 f1() ,其实执行的是 cou() ,而真正的 f1 函数的引用现在正被保存在 w1 函数中的 func参数里面,

    (这儿可以当作闭包的一个表现,即当函数中有东西外边还有引用指向它的时候,它并不会立即回收,而是保存了这个函数的空间)

    两层装饰: 例子:

    def w1(func):
        print("---正在装饰1----")
        def inner():
            print("---1111111111----")
            func()
        return inner
    
    def w2(func):
        print("---正在装饰2----")
        def inner():
            print("---2222222222----")
            func()
        return inner
    
    @w1
    @w2
    def f1():
        print("---f1---")
    
    >>>---正在装饰2----
       ---正在装饰1----

    从运行结果可以看出,首先调用装饰器w2,再调用装饰器w1,也就是说 运行到 @w1 这一行,因为在它下面的并不是一个函数,所以w1先暂停,先调用w2,w2装饰完成之后,返回的是w2 的 inner 函数的引用,

    w1 再开始对 w2 的inner 函数进行装饰. 最后返回的是w1 的 inner 函数.如果最后调用 f1()  那么运行结果为:

    ---正在装饰2----
    ---正在装饰1----
    ---1111111111----
    ---2222222222----
    ---f1---

    因为 这个时候调用 f1() 其实 调用的是 w1的 inner 函数,所以首先打印 --11111--- ,然后 执行 func() 这个func() 也就是 w2 的inner, 所以再打印 ---222222----, 下一句 fun() 才是真正的 f1() 函数,打印 ---f1---

    装饰有参数的函数: 被装饰的函数有参数的话,可以这样:

    def w1(func):
        print("---正在装饰1----")
        def inner(*args, **kwargs):
            print("---1111111111----")
            func(*args, **kwargs)
        return inner
    
    @w1
    def f1(a):
        print("---%d---" % a)
    f1(123)
    
    >>>---正在装饰1----
       ---1111111111----
       ---123---

    在 inner 函数里面加上 接受无名参数和关键字参数,然后 func(*args, **kwargs) 把接收到的参数原封不动的传回 f1 函数里面去,这样 f1 无论有多少个参数,都可以给他传回去.

    那么,如果被装饰的函数有返回值,同样,在 inner里面把函数返回的东西用个变量保存起来,然后 在inner 里面return 即可:

    def w1(func):
        print("---正在装饰1----")
        def inner(*args, **kwargs):
            print("---1111111111----")
            result = func(*args, **kwargs)    # <----------------------
            return result                     # <----------------------
        return inner
    
    @w1
    def f1(a):
        print("---%d---" % a)
        return 456
    a = f1(123)
    print(a)
    
    >>>---正在装饰1----
    ---1111111111----
    ---123---
    456

    可以看出 a 成功保存了返回的结果 456 .

    如果,对装饰器进行调用,如 @w1() 后面带个括号, 结果会怎样:

    def w1():
        print("---正在装饰1----")
        def inner(func):
            print("---1111111111----")
        return inner
    
    @w1()
    def f1():
        print("---f1---")
    
    >>>---正在装饰1----
       ---1111111111----

    可以看出,虽然没有调用f1,但是竟然连里面的inner函数也被执行了一遍,因为输出了 ---111111111111-----,这说明,如果 @w1() 这样用 ,那么它首先会 把 w1() 函数执行一遍 , 这个时候返回的是 inner 函数的引用,

    那么,@w1() 就变成了 @inner 这个时候 再把f1传到了inner函数里面开始进行装饰 所以 inner 函数被执行,

    利用这个特点,可以在 装饰器中带有参数 ,只不过为了防止调用,需要在外面再加上一层:

    def a1(nihao):
        def w1(func):
            print("---正在装饰1----")
            def inner():
                print("---1111111111----%s" % nihao)
                func()
            return inner
        return w1
    
    @a1('hello~')
    def f1():
        print("---f1---")
    
    >>>---正在装饰1----

    过程 1. 首先执行 a1('hello~')   a1里面用 nihao 这个变量保存传递的参数,返回的是 w1 的引用

            2. 装饰器那一行 变成了 @w1 ,然后把 f1 传递进去,调用 w1 开始进行装饰

      3. 装饰完成后 返回的 是 inner 的引用 所以 现在 f1 = inner

    如果调用 f1() 则正常执行,还可以在 inner 中把传递进去的参数打印出来:

    >>>
    ---正在装饰1----
    ---1111111111----hello~
    ---f1---
  • 相关阅读:
    一个关于状态机的问题
    8位同步码修改变4位同步码
    BT1120时序,可以用于自测用
    欧几理德,扩展欧几里德和模线性方程组。
    "旋转的风车"----windows(GDI)绘图
    草滩小恪的学习链接(汇总版)
    酒鬼随机漫步(一个矢量类)
    小题精炼-----初试C语言
    大二(上)------我欠青春一份疯狂
    HDU 1027 Ignatius and the Princess II(康托逆展开)
  • 原文地址:https://www.cnblogs.com/cccy0/p/8799491.html
Copyright © 2011-2022 走看看