10分钟搞定python装饰器!

python装饰器

概述

  装饰器是个很用的东西,但是对于初学者却没那么好理解,这里从最简单的任务入手,手把手告诉你装饰器是什么!

任务1

任务描述:你有一个函数foo(x,y),输入两个参数,可以打印输出两个数的和x+y,现在要求你给出一个新函数,使得输入两个参数,打印输出两个数的和差积商( + × ÷ )。
要求:不能改动原函数foo(x,y)

  这里为什么会有个不能改动原函数的要求呢?这是因为在实际工作学习中,有改动原函数工作量会很大!举个例子,这个代码很大很长,又或是这个代码是你很早以前写的,现在记忆不清楚了,在这些情况下,想要增加函数功能,就得先把原函数读懂,然后再修改,任务量不言而喻。

  这个任务是带参数的,略微有点复杂,这里埋个伏笔先不管这个任务1,我们现在从一个更简单的任务2入手,理解了这个小任务,再回过头来看我们的任务1,就迎刃而解了!

任务2

任务描述:你有一个函数bar(),输出一句话“这是函数bar”,现在要求你给出一个新函数,使得输出增加一句话(内容随意)。
要求:不能改动原函数foo(x,y)


  我们正式开始!

  这个bar()函数太简单了,这里直接给出:

def bar():
    print('这是函数bar')

  如果是没有不能改动原函数这个要求,那么增加一句话是这么操作的:在函数体里面增加代码:

def bar():
    print('这是随便写的一句话')
    print('这是函数bar')

  现在有了不能改动原函数这个要求,该怎么做呢?我们可以这么做:把函数bar丢给另外一个函数,用这个另外的函数给bar包一层代码。
  来,看看这段代码能读懂不:

def dec(aaa):
    def in_dec():
        print('这是随便写的一句话')
        aaa()
    return in_dec


def bar():
    print('这是函数bar')


bar = dec(bar)
bar()

  这段代码的难点在于读懂函数dec(aaa),函数里面还有一个函数。看似可怕,但其实都是纸老虎,函数就只是函数而已,无非是复杂一点,并不是难。下面一起来读懂这段代码!

  首先可以肯定的是,函数dec(aaa)有一个返回值,这个返回值不是整数int,也不是字符串str,而是一个函数。所以,

bar = dec(bar)

  这句代码做的事是:把函数bar当做参数给了函数decdec执行完后输出(return)了一个函数(不是int,不是str),并把这个函数赋值给了bar。所以,=号左边的bar是一个与原bar函数不同的函数,是经过函数dec处理过的新函数。

  接着来,那么这个返回的函数是什么呢?看dec(aaa)函数体内部:

def dec(aaa):
    def in_dec():
        print('这是随便写的一句话')
        aaa()
    return in_dec

  可以看出,返回的是dec(aaa)函数内部的那个函数in_dec(),也就是说,新bar = in_dec(),也就是说,执行新bar等于执行in_dec()

  那么,我们这个in_dec()函数到底是什么呢?我们把传入dec(aaa)的参数考虑进来,可以看出,丢给dec一个参数(一个函数),这个丢进来的函数就成了in_dec()的一部分。拿我们这句代码来讲:

bar = dec(bar)

  就是,把原bar丢给了dec,dec执行的结果是,原bar进了in_dec函数,替换掉了aaa(),所以,in_dec()最终实际上等于:

    def in_dec():
        print('这是随便写的一句话')
        bar()

而这个in_dec()又return给了新bar,所以,新bar实际等于in_dec,写的再直白一点就是:

    defbar():
        print('这是随便写的一句话')
        原bar()

到这里,代码已经分析地很透彻了,我们执行一下新bar(),结果也就很明显了:

bar()
这是随便写的一句话
这是函数bar

  代码分析完成了,应该说分析的很清楚透彻了,下面开始引入我们的主角——装饰器。

刚刚的这段代码:

def dec(aaa):
    def in_dec():
        print('这是随便写的一句话')
        aaa()
    return in_dec


def bar():
    print('这是函数bar')


bar = dec(bar)
bar()

在python中,可以用另外一种方式来写,意思完全一样:

def dec(aaa):
    def in_dec():
        print('这是随便写的一句话')
        aaa()
    return in_dec

@dec
def bar():
    print('这是函数bar')

bar()

  自己执行一下,结果确实是一样的。
  可以看出,两段代码区别其实很小,无非就是用一个@dec代替了bar = dec(bar)。这两个的意思是一模一样的,写法不一样,无非就是格式问题,本质是一样的。
  那么,这个用@dec来给bar增加功能的这些代码,就叫装饰器
  其实也很好理解,装饰就是加点小玩意儿的意思嘛,这不就是给函数增加了点功能嘛,所以用来增加功能的这些代码就叫装饰器
  所以在这里,我给装饰器下一个定义:装饰器就是在不改动原函数的前提下,以原函数为核心,给原函数增加功能的代码。


好,到这里,我们的装饰器的核心内容就讲完了。
我们可以自己做个小练习:

练习题

给以下几个函数增加一句话,内容随意:

def bar():
    print('这是函数bar')


def bar2():
    print('这是函数bar2')


def bar3():
    print('这是函数bar3')


def bar4():
    print('这是函数bar4')

答案:

def dec(aaa):
    def in_dec():
        print('这是随便写的一句话')
        aaa()
    return in_dec

@dec
def bar1():
    print('这是函数bar1')

@dec
def bar2():
    print('这是函数bar2')

@dec
def bar3():
    print('这是函数bar3')

@dec
def bar4():
    print('这是函数bar4')


bar1()
bar2()
bar3()
bar4()
这是随便写的一句话
这是函数bar1
这是随便写的一句话
这是函数bar2
这是随便写的一句话
这是函数bar3
这是随便写的一句话
这是函数bar4

一些说明

在解释装饰器的时候,为了力图容易理解,有一些细节并没有说出来,现在在这里统一说明

  1. 装饰器涉及到闭包函数的概念,在我讲的例子里,in_dec就是一个闭包函数。闭包函数可以简单地理解为:使用了上级函数(dec)变量的内函数。其中这个变量的问题,又涉及到LEGB原则,想要深入理解的读者可以自己再查,这里不再赘述。
  2. 装饰器的英文是decorator,这也是我的dec函数的名字来源。
  3. 没了。

  我们开始解决我们的任务1(其实如果前面的学懂了,这里可以不用看讲解部分了,直接看答案,当做个练习),foo(x,y)函数非常简单,直接给出:

def foo(x, y):
    print('x+y =', x+y)

  为了照顾新手,从最基本的赋值方法foo = dec(foo)讲,可以强化对原理的理解。
  根据之前讲的,自己先写一下,重点注意带参数情况该怎么写,我这里给出个代码框架,参数栏我用?代替。

def dec(aaa):
    def in_dec(?):
        aaa(?)
        print('x-y =', x - y)
        print('x*y =', x * y)
        print('x/y =', x / y)
    return in_dec


def foo(x, y):
    print('x+y =', x+y)


foo = dec(foo)
foo(1, 2)

答案公布,两个问号的地方,都需要写x,y

def dec(aaa):
    def in_dec(x, y):
        aaa(x, y)
        print('x-y =', x - y)
        print('x*y =', x * y)
        print('x/y =', x / y)
    return in_dec


def foo(x, y):
    print('x+y =', x+y)


foo = dec(foo)
foo(1, 2)

运行一下

x+y = 3
x-y = -1
x*y = 2
x/y = 0.5

下面解释一下参数为什么要这么写。
dec函数

def dec(aaa):
    def in_dec(?):
        aaa(?)
        print('x-y =', x - y)
        print('x*y =', x * y)
        print('x/y =', x / y)
    return in_dec

  我们知道,aaa(?)是要用原foo(x,y)替换掉的,那么这里的aaa肯定是要加参数的。再看in_dec(?),因为函数体里需要用两个参数,所以这里也要加参数。

  好的,这个任务到这里基本完成了,还差最后一步:用装饰器的语法把上面的代码改写一下,也就是我们最终的答案。

任务1答案

def dec(aaa):
    def in_dec(x, y):
        aaa(x, y)
        print('x-y =', x - y)
        print('x*y =', x * y)
        print('x/y =', x / y)
    return in_dec


@dec
def foo(x, y):
    print('x+y =', x+y)


foo(1, 2)
x+y = 3
x-y = -1
x*y = 2
x/y = 0.5

猜你喜欢

转载自blog.csdn.net/lllxxq141592654/article/details/81057400