闭包及装饰器基础

闭包

定义
  • 闭是封闭(函数中的函数),包是包含(该内部函数对外部函数作用域而非全局作用域变量的引用)

  • 什么是闭包

    • 内部函数对外部函数作用域里的变量的引用
    • 函数内的属性,都是有生命周期(在函数执行期间的)
    • 闭包内的闭包函数私有化了变量,完成了数据的封装,类似面向对象
引入
def foo():
    print("in foo()")

    def bar():
        print("in bar()")
#1.直接运行内部函数报错
#bar()
#2.考虑先运行外部函数,再运行内部函数,依然会报错,外部函数在结束运行后资源又被释放
#foo()
#bar()

由于作用域的问题,函数内的属性都是有生命周期的,只有在函数执行期间

再考虑这段代码,只有调用foo()是,内部的print()及bar()才能存活

def foo():
    print("in foo()")
    def bar():
        print("in bar()")
    return bar  #将bar函数体作为返回值返回
var = foo() #此时var等同与bar
var()		#执行var()等同于执行bar()
#结果
in foo()
in bar()
-------------------------------------------------------
# 前面说,内部函数对外部函数作用域变量的引用
def foo():
    a = 66
    print("in foo()")

    def bar(num):
        print("in bar()")
        print(a + num)

    return bar

var = foo() #此时var就是var
var(22)		#等价于直接执行bar(20)
print(a)
#结果
in foo()
Traceback (most recent call last):
in bar()
88
    print(a)  #脱离函数体,a为局部变量,报错
NameError: name 'a' is not defined

当bar()函数能够有连续操作时

li = [1, 2, 3, 4, 5]
def foo(obj):
    print("foo:", obj)
    def bar():
        obj[0] += 1
        print("bar:", obj)
    return bar
var = foo(li)
var()
var()
#结果
foo: [1, 2, 3, 4, 5] #直接执行foo(li),并将bar返回给var
bar: [2, 2, 3, 4, 5] #第一次操作,obj[0]=1+1=2
bar: [3, 2, 3, 4, 5] #第二次操作,obj[0]=2+1=3

装饰器

引入

有时候需要在不影响原函数的情况下,对于函数继续功能扩展

实例

需要计算每个程序运行的时间

import time
def counts():
    s=0
    for i in range(100001):
        s+=i
    print('sum:%d'%s)
start =time.time()
counts()
end=time.time()
print('执行时间:',(end-start))

sum:5000050000
执行时间: 0.008973836898803711

​ 成功的实现时间的计算,但是如果有成千上万个函数每个函数都怎么写一遍,非常麻烦
代码量也会凭空增加很多。

def use_time(func):
    start = time.time()
    func()
    end = time.time()
    print('执行时间:',(end-start))

经过修改,定义一个函数来实现时间计算的功能,简化了之前的操作,但是在使用时,还是需要将函数传入到时间计算函数中

def  count_time(func):
    def wrapper():
        start=time.time()
        func()
        end=time.time()
        print('执行时间:', (end - start))
    return wrapper
a=count_time(counts)
a()

使用闭包,在使用时,让 counts 函数重新指向了 count_time 函数返回后的函数引用。这样在使用 counts 函数时,就和原来使用方式一样了。

import time
def  count_time(func):
    def wrapper():
        start=time.time()
        func()
        end=time.time()
        print('执行时间:', (end - start))
    return wrapper

@count_time
def counts():
    s=0
    for i in range(1000001):
        s+=i
    print ('sum:%d'%s)

counts()

sum:500000500000
执行时间: 0.08475852012634277

使用装饰器,再调用counts时,同时调用了count_time,并且返回其中

在使用时,让counts函数重新指向count_time函数返回的函数调用。只有counts就和原来的使用方式一样了

实现就是采用装饰器

装饰器几种形式
1.无参数无返回值
def setFunc(func):
    def wrapper():
        print('start')
        func()
        print('end')
    return wrapper

@setFunc
def show ():
    print('in show')
show()
##结果:
start
in show
end
2.无参数有返回值
def setFunc(func):
    def wrapper():
        print('start')
        func() #此时func()没有被任何其他接收,直接被释放,如果在此处return,则下面的end不会打印   
        print('end')
        return func()#将func()返回,上面的func()没有用,可以直接省去
    return wrapper

@setFunc
def show ():
    return  'in show'
print(show())
start
end
in show  #如果没有return func则为None
3.有参数有返回值
def setFunc(func):
    def wrapper(s):
        print('start')
        func(s)
        print('end')
        return func('jc') #执行func(jc),返回None
    return wrapper

@setFunc
def show (s):
    print('hello %s'%s)
show('Bob')
#结果
start
hello Bob
end
hello jc
4.有参数有返回值
def setFunc(func):
    def wrapper(a,b):
        print('start')
        print('end')
        return func(a,b)
    return wrapper
@setFunc
def add(x,y):
    return x+y
print(add(5,6))
#结果
start
end
11
5.万能装饰器

根据被装饰函数的定义不同,分出了四种形式,

定义万能装饰器,能够满足任意形式的函数定义

def setFunc(func):
    def wrapper(*args,**kwargs): #可变参数,接收不同参数类型
        print('wrapper context.')
        return func(*args,**kwargs)
    return wrapper
@setFunc
def func(name,age,job='student'):
    print(name,age,job)
func('bob',17)
@setFunc
def stp(a,b,*c,**d):
    print(a,b)
    print(c)
    print(d)
stp('cy','clg',1991,11,11,tk='ul')
##结果:
wrapper context.
bob 17 student
wrapper context.
cy clg
(1991, 11, 11)
{'tk': 'ul'}
函数被多个装饰器装饰

一个函数在使用时,通过一个装饰器来拓展,可能并不能达到预期。

一个函数被多个装饰器所装饰。

def setFunc1(func):
    def wrapper1(*args,**kwargs):
        print('Wrapper Context 1 start.'.center(42,'-'))
        func(*args,**kwargs)
        print('Wrapper Context 1 end.'.center(42,'-'))
    return wrapper1
def setFunc2 (func):
    def wrapper2(*args, **kwargs):
        print('Wrapper Context 2 start.'.center(42, '-'))
        func(*args, **kwargs)
        print('Wrapper Context 2 end.'.center(42, '-'))
    return wrapper2

@setFunc2   #F
@setFunc1   #g

def show(*args,**kwargs):  #f
    print('show R.'.center(42))
show()  #F(g(f))
#结果:
---------Wrapper Context 2 start.---------
---------Wrapper Context 1 start.---------
                 show R.                  
----------Wrapper Context 1 end.----------
----------Wrapper Context 2 end.----------
  • 程序语句运行顺序:自下而上的修饰。先用F1装饰,再以结果为整体,用F2再装饰

  • 函数调用来看:从内往外 。类似于复合函数

总结

这样实现的好处是,定义好了闭包函数后。只需要通过 @func 形式的装饰器语法,将 @func 加到要装饰的函数前即可。

使用者在使用时,根本不需要知道被装饰了。只需要知道原来的函数功能是什么即可。

这种不改变原有函数功能基础上,对函数进行扩展的形式,称为装饰器。

在执行 @func时 ,实际就是将 原函数传递到闭包中,然后原函数的引用指向闭包返回的装饰过的内部函数的引用。

回顾总结

  • 函数可以像普通变量一样,作为函数的参数或者返回值进行传递。
  • 函数的内部可以定义另外一个函数。目的,隐藏函数功能的实现。
  • 闭包也是函数的一种定义形式
  • 闭包定义的规则,再外部函数定义一个内部函数,内部函数使用外部函数的变量,并返回内部函数的引用
  • python中,装饰器就是通过闭包来实现的
  • 装饰器的作用是在不改变原有函数的基础上,为函数增加功能
  • 装饰器的使用,通过@装饰器函数名的形式来给已有函数进行装饰,并添加功能
  • 装饰器根据参数以及返回值可以分为四种,万能装饰器通过可变参数(*args,**kwargs)来实现
  • 一个装饰器可以为多个函数提供装饰的功能,一个函数也可以使用多个装饰器
  • 通过类可以实现装饰器:重写__init____call__函数
  • 类装饰器在装饰函数后,原来的引用不再是函数,而是装饰器的对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值