python中闭包与装饰器

前几天学习python装饰器时,看各种例子,上来就是一个嵌套函数,还返回一个函数对象(返回内嵌函数),学得我是一脸懵逼啊,觉得太难懂了。今天又来学习,才发现,原来要理解装饰器,先得了解闭包的知识。没错,就是闭包。
闭包又是个什么东西啊?怎么好像更难懂啊。别着急。。。其实我之前没搞懂的那个嵌套函数就是闭包的写法,下面记录了学习笔记,来一起学习吧。

闭包(closure)

认识闭包

咱们用好理解一点的Python的语言介绍一下,一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫做闭包。你在调用函数A的时候传递的参数就是自由变量
举个栗子:

def func(name):
    def inner_func(age):
        print 'name:', name, 'age:', age
    return inner_func

bb = func('lucy')
bb(26)  
# >>> name: lucy age: 26

这里面调用func的时候就产生了一个闭包——inner_func,并且该闭包持有自由变量——name,因此这也意味着,当函数func的生命周期结束之后,name这个变量依然存在,因为它被闭包引用了,所以不会被回收。

闭包的作用

闭包的最大特点是可以将父函数的变量与内部函数绑定,并返回绑定变量后的函数(也即闭包),此时即便生成闭包的环境(父函数)已经释放,闭包仍然存在,这个过程很像类(父函数)生成实例(闭包),不同的是父函数只在调用时执行,执行完毕后其环境就会释放,而类则在文件执行时创建,一般程序执行完毕后作用域才释放,因此对一些需要重用的功能且不足以定义为类的行为,使用闭包会比使用类占用更少的资源,且更轻巧灵活。

练习

如下代码,求和,求平均数:

#!/usr/bin/python
# -*-coding:utf-8-*-

def my_sum(*arg):
    a = sum(arg)
    print a
    return a
def my_average(*arg):
    b = sum(arg)/len(arg)
    print b
    return b

my_sum(1,2,3,4,5)
my_average(1,2,3,4,5)

当调用my_sum时传入的参数有str,调用my_average时传入的参数为空时代码会报错,所有增加代码判断传入的参数

#!/usr/bin/python
# -*-coding:utf-8-*-

def my_sum(*arg):
    if len(arg) == 0:
        return 0
    for val in arg:
        if not isinstance(val, int):
            return 0
    a = sum(arg)
    print a
    return a
def my_average(*arg):
    if len(arg) == 0:
        return 0
    for val in arg:
        if not isinstance(val, int):
            return 0
    b = sum(arg)/len(arg)
    print b
    return b

my_sum(1,2,3,4,5)
my_average(1,2,3,4,5)
my_sum(1,2,3,4,5,'6')   # 这2个调用都会报错,所有要加判断,来判断输入的参数
my_average()

那么问题来了,以上2个函数,每个函数中对于传入参数的判断代码是重复的,一旦修改,就需要多个地方修改,工作量就大了,对于代码的维护也不便。那么怎么优化代码了,这时候就可以引入闭包了

#!/usr/bin/python
# -*-coding:utf-8-*-

def my_sum(*arg):
    a = sum(arg)
    print a
    return a
def my_average(*arg):
    b = sum(arg)/len(arg)
    print b
    return b

def dec(func):
    def in_dec(*arg):
        if len(arg) == 0:
            return 0
        for val in arg:
            if not isinstance(val, int):
                return 0
        func(*arg)
    return in_dec

my_sum = dec(my_sum)
my_average =dec(my_average)

my_sum(1,2,3,4,5)
my_average(1,2,3,4,5)
my_sum(1,2,3,4,5,'6')   # 这2个调用都会报错,所有要加判断,来判断输入的参数
my_average()

装饰器(Decorator)

认识装饰器

“装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数”

其实装饰器就是对闭包的使用

特点:

1、装饰器是用例装饰函数;
2、返回一个函数对象;
3、被标识函数标识符指向:返回的函数对象;
4、语法糖 @

练习

上面闭包的例子,使用装饰器后的代码如下:

#!/usr/bin/python
# -*-coding:utf-8-*-

def dec(func):
    def in_dec(*arg):
        if len(arg) == 0:
            return 0
        for val in arg:
            if not isinstance(val, int):
                return 0
        func(*arg)
    return in_dec

@dec
def my_sum(*arg):
    a = sum(arg)
    print a
    return a
@dec
def my_average(*arg):
    b = sum(arg)/len(arg)
    print b
    return b

# my_sum = dec(my_sum)
# my_average =dec(my_average)

my_sum(1,2,3,4,5)
my_average(1,2,3,4,5)
my_sum(1,2,3,4,5,'6')   # 这2个调用都会报错,所有要加判断,来判断输入的参数
my_average()

实际就是将调用dec函数,使用@dec代替

# my_sum = dec(my_sum)
# my_average =dec(my_average)

调用过程分析:
1、@dec后,my_sum作为一个参数传给dec,dec(my_sum) 返回一个函数对象in_dec
2、返回的函数对象in_dec,由谁来接收呢?my_sum 原来指向的是求和函数,但现在my_sum 作为一个参数传给了dec函数,然后他当作一个enclosing作用域的变量(需要了解函数作用域LEGB),被in_dec函数所使用,so,my_sum 重新赋了值,my_sum = in_dec
3、my_average()实际是调用in_dec(),且装饰器里重新调用了my_average()函数

参考资料:
https://segmentfault.com/a/1190000004461404

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容