python的闭包及装饰器

时间:2020-12-08 22:47:27


闭包:


闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了*变量的函数。这个被引用的*变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体

1、函数是一个对象

2、函数执行完成后内部变量回收

3、函数属性

4、函数的返回值

实例一、

分别检测分数科目总分为100、150两种情况的成绩

初级代码如下:

# -*- coding:utf-8 -*-
###检测分数总数为100分的及格情况
def ck_100(val):
    passline = 60
    if val >= passline:
        print "当前科目总分为100,你的成绩为: %d,成绩通过 " %val
    else:
        print "当前科目总分为100,你的成绩为: %d,成绩不通过 " %val

###检测分数总数为150分的及格情况
def ck_150(val):
    passline = 90
    if val >= passline:
        print "当前科目总分为150,你的成绩为: %d,成绩通过 " %val
    else:
        print "当前科目总分为100,你的成绩为: %d,成绩不通过" %val

#100总分科目
ck_100(90)
ck_100(55)
#150总分科目
ck_150(110)
ck_150(88)

执行结果如下:

>>> ================================ RESTART ================================>>> 当前科目总分为100,你的成绩为: 90,成绩通过 当前科目总分为100,你的成绩为: 55,成绩不通过 当前科目总分为150,你的成绩为: 110,成绩通过 当前科目总分为100,你的成绩为: 88,成绩不通过>>>


使用闭包优化后的代码:

# -*- coding:utf-8 -*-###根据不同科目设置及格线def set_passline(passline):###检测传入分数是否及格    def cmp(val):        if val >= passline:            print "及格线为: %d, 您的分数为: %d ,恭喜及格" %(passline,val)        else:            print "及格线为: %d, 您的分数为: %d ,遗憾不及格" %(passline,val)    return cmp#总分为100的科目score_100 = set_passline(60)score_100(89)score_100(55)#总分为150的科目score_150 = set_passline(90)score_150(99)score_150(88)


执行结果:

>>> ================================ RESTART ================================>>> 及格线为: 60, 您的分数为: 89 ,恭喜及格及格线为: 60, 您的分数为: 55 ,遗憾不及格及格线为: 90, 您的分数为: 99 ,恭喜及格及格线为: 90, 您的分数为: 88 ,遗憾不及格


Tips:

可以看代码量少了一半;

闭包就是我们内置函数对 enclosing 作用域  变量  的使用


实例二、

# -*- coding:utf-8 -*-###求成绩总分def my_sum(*args):    return sum(args)###求成绩平均分def my_average(*args):    return sum(args)/len(args)print my_sum(1,2,3,4,5)print my_average(1,2,3,4,5)

执行结果:

>>> ================================ RESTART ================================>>> 153>>>

发现上述代码不够健壮,如果你传入字符串或空的列表就会报错

print my_sum(1,2,3,4,5,'6')
    return sum(args)TypeError: unsupported operand type(s) for +: 'int' and 'str'>>>

修改代码,进行检查传入数据的类型及长度

# -*- coding:utf-8 -*-###求成绩总分def my_sum(*args):    if len(args) == 0:        return 0    for val in args:        if not isinstance(val,int):            print "传入参数有非整数性..."            return 0    return sum(args)###求成绩平均分def my_average(*args):    if len(args) == 0:        return 0    for val in args:        if not isinstance(val,int):            print "传入参数有非整数性..."            return 0    return sum(args)/len(args)print my_sum(1,2,3,4,5)print my_average(1,2,3,4,5)print my_sum(1,2,3,4,5,'6')

执行结果:

>>> ================================ RESTART ================================>>> 153传入参数有非整数性...0

健壮性是可以了,不过发现其中有一部门代码是重复的,但是执行的 函数 又是不同的,分别为 my_sum 、my_average


使用闭包优化后的代码:

# -*- coding:utf-8 -*-###求成绩总分def my_sum(*args):    return sum(args)###求成绩平均分def my_average(*args):    return sum(args)/len(args)def dec(func):    def in_dec(*args):        if len(args) == 0:            print "传入参数列表为0"            return 0        for val in args:            if not isinstance(val,int):                print "传入非整数参数"                return 0        return func(*args)    return in_dec#重新定义my_summy_sum = dec(my_sum)#此处调用dec,作用就是将 参数为函数的 func 变为 in_dec 函数的一个属性 ( enclosing 属性 ),并且将 in_dec 函数本身返回,返回的函数还具有 in_dec 本身的功能属性 (检测参数的作用)#并且 my_sum 进行了重新定义,返回值为 传入函数处理后的 返回值print my_sum(1,2,3,4,5)print my_sum()print my_sum(1,2,3,4,5,'6')#重新定义my_averagemy_average = dec(my_average)print my_average(1,2,3,4,5)print my_average()print my_average(1,2,3,4,5,'6')

执行结果:

>>> ================================ RESTART ================================>>> 15传入参数列表为00传入非整数参数03传入参数列表为00传入非整数参数0>>>

装饰器:

1、用来装饰函数

2、返回一个新的函数对象

3、被装饰函数标识符指向返回的函数对象

4、语法糖: @deco

上述闭包的例子,in_dec 被 dec 所装饰,那可以说装饰器其实就是闭包的一个本质使用


为了演示 装饰器 的功能,以上述代码为例,进行 装饰器 写法

# -*- coding:utf-8 -*-def dec(func):    print "call dec"    def in_dec(*args):        print "call in_dec"        if len(args) == 0:            print "传入参数列表为0"            return 0        for val in args:            if not isinstance(val,int):                print "传入非整数参数"                return 0        return func(*args)    return in_dec    @dec###求成绩总分def my_sum(*args):    return sum(args)###求成绩平均分def my_average(*args):    return sum(args)/len(args)

所以没有显示调用,但是执行以下:

>>> ================================ RESTART ================================>>> call dec>>>

结果证明 调用 了 dec 函数,那应该是 装饰器语法糖 @dec 时进行了调用,但是它返回了一个函数,那被谁接受了呢? 是被他装饰的 my_sum 接受了。

为了证明,来调用下 my_sum

print my_sum(1,2,3,4,5)print my_sum(1,2,3,4,5,'6')

结果:

>>> ================================ RESTART ================================>>> call dec      ####### 语法糖调用一次装饰函数 deccall in_dec15call in_dec传入非整数参数0>>>

证明是对的;

@dec 相当于上述的

my_sum = dec(my_sum)

也就可以认为 被重新赋值的 my_sum 相当于装饰器 dec 的内部函数 in_dec

装饰器 dec 的参数 func 相当于 被修饰的函数 my_sum

装饰器 实例二、

# -*- coding:utf-8 -*-def dec(func):    print "call dec"    def in_dec():        print "call in_dec"        func()@decdef test_dec():    print "call test_dec"print type(test_dec)test_dec()

执行结果:

>>> ================================ RESTART ================================>>> call dec<type 'NoneType'>Traceback (most recent call last):  File "D:\xisuo\xx\q.py", line 15, in <module>    test_dec()TypeError: 'NoneType' object is not callable>>>

可以看到这里的 test_dec 是 NoneType类型,无法调用,那是因为我们在装饰器函数没有进行显示的 return ,python 默认返回 None,

所以装饰器函数必须显示的进行 return in_func (这里为 return in_dec)


修改后 执行

# -*- coding:utf-8 -*-def dec(func):    print "call dec"    def in_dec():        print "call in_dec"        func()    return in_dec      #结论:必须返回@decdef test_dec():    print "call test_dec"print type(test_dec)test_dec()


>>> ================================ RESTART ================================>>> call dec<type 'function'>call in_deccall test_dec>>>

上面提到了(红字)函数间的关系(相当于对象引用关系),那赋值的被修饰函数的 参数 就必须三个函数都对应起来

# -*- coding:utf-8 -*-def dec(func):    print "call dec"    def in_dec(x,y):      ##参数要对应起来        print "call in_dec"        func(x,y)       ##参数要对应起来    return in_dec@decdef test_dec(x,y):     ##参数要对应起来    print "call test_dec ",x + yprint type(test_dec)test_dec(3,5)

执行结果:


>>> ================================ RESTART ================================>>> call dec<type 'function'>call in_deccall test_dec  8>>>


本文出自 “大�L” 博客,请务必保留此出处http://lansgg.blog.51cto.com/5675165/1884913