python函数3(闭包、装饰器)

python函数

一、闭包

       1、函数的嵌套

       如果在一个函数的内部定义了另一个函数,外部的我们叫它外函数,内部的我们叫它内函数。

        2、闭包的概念

       在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。

       3、闭包的功能

       一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
 
def outer(a):            #  outer是外部函数 a和b都是外函数的临时变量
    b = 10
    def inner():         #  inner是内函数
        print(a+b)       #  在内函数中 用到了外函数的临时变量
    return inner      #  外函数的返回值是内函数的引用

if __name__ == '__main__':
     '''
      在这里我们调用外函数传入参数5,此时外函数两个临时变量 a是5 b是10,
     并创建了内函数,然后把内函数的引用返回存给了demo
     外函数结束的时候发现内部函数将会用到自己的临时变量,
     这两个临时变量就不会释放,会绑定给这个内部函数
    '''
    demo = outer(5)
     # 我们调用内部函数,看一看内部函数是不是能使用外部函数的临时变量
     # demo存了外函数的返回值,也就是inner函数的引用,这里相当于执行inner函数
    demo()     # 15
    demo2 = outer(7)
    demo2()     #17

  

       4、闭包的机制
       我们在python中定义一个函数def demo():  的时候,内存当中会开辟一些空间,存下这个函数的代码、内部的局部变量等等。这个demo只不过是一个变量名字,它里面存了这个函数所在位置的引用而已。我们还可以进行x = demo, y = demo, 这样的操作就相当于,把demo里存的东西赋值给x和y,这样x 和y 都指向了demo函数所在的引用,在这之后我们可以用x() 或者 y() 来调用我们自己创建的demo() ,调用的实际上根本就是一个函数,x、y和demo三个变量名存了同一个函数的引用。
 
       对于闭包,在外函数outer中 最后return inner,我们在调用外函数 demo = outer() 的时候,outer返回了inner,inner是一个函数的引用,这个引用被存入了demo中(demo=inner)。所以接下来我们再进行demo() 的时候,相当于运行了inner函数。
 
 5、内函数处理数据
       在基本的python语法当中,一个函数可以随意读取全局数据,但是要修改全局数据的时候有两种方法:1 global 声明全局变量 2 全局变量是可变类型数据的时候可以修改。
  
       在闭包内函数也是类似的情况。在内函数中想修改闭包变量(外函数绑定给内函数的局部变量)的时候,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。
 
 
 二、装饰器

       python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。

        1、有这样一段代码,统计一段代码的执行时间,比如打印1-100000执行时间,我们可以使用一个函数来封装。
import time

def calc_time(fn):
    start_time=time.time()
    fn()
    end_time=time.time()
    print(f'代码消耗时间:{end_time-start_time}')

def demo():
    for i in range(100000):
        print(i)

calc_time(demo)

  在Python中,能把代码更加优化,使用装饰器。

import time

def calc_time(fn):
    print('我是外函数!')
    def inner():
        start_time = time.time()
        fn()
        end_time=time.time()
        print(f'代码消耗时间:{end_time-start_time}')
    return inner

@calc_time 
def demo():
    for i in range(100000):
        print(i)

demo()

  执行步骤:

       a、把被装饰函数demo作为参数传给装饰器(fn=demo)

       b、底层执行装饰器函数(calc_time(demo))

       c、装饰器函数执行完,return 内函数(inner),此时底层再次用demo来接return出来的对象(demo=inner)

       d、第1—第3步骤可概况为:demo=calc_time(demo)

       e、最后执行demo()即执行inner()

       2、被装饰函数传参(万能装饰器)

import time
sum=0
def calc_time(fn):
    def inner(*args,**kwargs):
        start_time = time.time()
        fn(*args,**kwargs)
        end_time=time.time()
        print(f'代码消耗时间:{end_time-start_time}')
    return inner

@calc_time      #demo=calc_time(demo)
def demo(*args,**kwargs):
    global sum
    for i in range(*args):
        sum+=i
    print(sum)
    print(kwargs)

demo(33333,a=55,b=222)

  

       3、装饰器传参

def outer(clock):
    print('我是外函数')

    def middle(fn):
        print('我是middle函数')

        def inner(*args,**kwargs):
            name=args[0]
            age=kwargs['age']
            print('我是内函数')
            print(f'外函数的参数是{clock}')
            print(f'{name}今年{age}岁')
            fn()
        return inner

    return middle

@outer(12)                      
def test():                     
    print('多重装饰器的使用测试')

test('zhangsan',age=20)    

运行结果:
        我是外函数
        我是middle函数
        我是内函数
        外函数的参数是12
        zhangsan今年20岁
        多重装饰器的使用测试

  执行步骤:

       a、执行outer函数,把12传递给clock,return middle对象

       b、@middle就是一个装饰器,把test函数传递给fn

       c、底层执行装饰器,return 内函数(inner),此时底层再次用test来接return出来的对象(test=inner)

       d、test('zhangsan',age=20)  就是执行inner('zhangsan',age=20)

       4、多重装饰器

def fun1(fun):
    def inner_fun1():
        fun()
        print('----inner_fun1----')

    return inner_fun1


def fun2(fun):
    def inner_fun2():
        fun()
        print('----inner_fun2----')

    return inner_fun2


@fun2
@fun1
def fun():
    print('----fun----')


fun()

  运行结果:

----fun----
----inner_fun1----
----inner_fun2----

  执行步骤:

       1、fun作为参数传给装饰器fun1

       2、底层开始执行装饰器fun1,把内函数inner_fun1返回出来,此时fun=inner_fun1

       3、再把当前的fun(inner_fun1)作为参数传给装饰器fun2

       4、底层执行装饰器fun2,把内函数inner_fun2返回出来,此时fun=inner_fun2

       5、开始执行fun(),实际上就是执行inner_fun2(),inner_fun2函数里有两条语句,第一条是fun(),第二条是print('inner_fun2'),而fun为2里面的inner_fun1,也就是执行inner_fun1函数

       6、inner_fun1函数里也有两条语句,第一条是fun(),第二条是print('inner_fun1'),此时的fun函数即为原始的被装饰函数,因此输出的顺序为:

     ----fun----
     ----inner_fun1----
     ----inner_fun2----

       7、使用装饰器的效果等同于:fun2(fun1(fun))()

原文地址:https://www.cnblogs.com/shixiaoxun/p/14424117.html