一、闭包函数

1.闭包基础

前面我们已经学习了函数嵌套,我们再来看一个关于函数嵌套的例子。

外层函数outer_1定义了1个空列表lst,然后调用内层函数inner_1,每次调用时往内层函数传入参数1,inner_1在执行时,lst中添加一个1,然后通过外层函数将lst返回给调用函数。

def outer_1():lst = []def inner_1(num1):lst.append(num1)inner_1(1)return lst
f1 = outer_1()
f2 = outer_1()
print(f1)
print(f2)# 输出:
[1]
[1]

我们发现,f1的值为[1],f2的值也为[1],lst的值每次运行都会初始化为空列表,有没有办法让lst可以持续保存存入其中的元素呢?

局部变量在函数执行完后,会在内存中自动释放掉,如果希望局部变量可以长期存在,我们就可以使用闭包函数。

接下来看看闭包函数的结构。

def outer_1():                 # 动作2 进入outer_1执行过程lst = []                   # 动作3 生成一个空列表 print("I am outer_1")def inner_1(num1):         # 动作4 定义一个名为inner_1的内层函数 # 动作8 将参数1传给num1 # 动作13 将参数1传给num1lst.append(num1)       # 动作9 向lst添加元素1   # 动作14 向lst再添加元素1return lst             # 动作10 将lst返回       # 动作15 将lst返回return inner_1             # 动作5 将函数inner_1返回,此时f1获得inner_1函数
f1 = outer_1()                 # 动作1 调用函数outer_1  # 动作6 将返回的结果赋值给f1
print(f1(1))                   # 动作7 调用函数f1(1)    # 动作11 将返回的lst打印出来
print(f1(1))                   # 动作12 再次函数f1(1)   # 动作16 将返回的lst打印出来# 输出
I am outer_1
[1]
[1, 1]print(type(f1)) # 输出 <class 'function'>
print(f1) # 输出 <function outer_1.<locals>.inner_1 at 0x109aeb550>

外层函数定义1个空列表lst,内层函数引用外层函数的lst变量,使用append添加元素,并将lst返回,外层函数的最后一行是将内层函数名作为一个对象返回。

我们来仔细看下程序是怎样执行的:

动作1: f1=outer_1(),先执行等号右边的函数

动作2:进入outer_1执行过程

动作3:生产一个名为lst的空列表

动作4:定义一个名为inner_1的内层函数

动作5:将函数inner_1返回

动作6:将返回的结果赋值给f1,此时f1获得了inner_1这个内层函数

动作7:调用函数f1(1),传入一个参数给inner_1()

动作8:直接进入内部函数,形参num接收到数值1

动作9:向lst添加元素1

动作10:返回lst

动作11:打印返回的lst

我们发现外层函数只有在f1=outer_1()的赋值过程中才执行,之后f1(1)只是对内层函数inner_1的调用。第二次执行f1(1)的过程基本相似,只是在第二次执行时,lst并没有从内存中删除,依然保留着第一次执行时的结果,所以在第二次执行后,lst的值是[1,1]。

理解这个过程如果感觉有点晕,还有一个好方法是将以上代码逐行debug观察执行过程,理解起来就简单的多了。

2.闭包形成条件

如果在一个函数内部,嵌套了函数,这个内部函数对外部作用域(非全局作用域)的变量进行引用,那么这个内部函数称为闭包。闭包每次运行时能记住引用的外部作用域的变量的值。

闭包的形成条件:

(1)一个函数嵌套了另一个函数;

(2)内层函数引用了外层函数的变量;

(3)外层函数将内层函数作为返回值返回。

3.自由变量

在上面的例子中,inner_1引用了外层函数outer_1的变量lst,一旦外部函数outer_1被执行,inner_1就成为了闭包函数,被内层函数引用的变量lst称为自由变量。

自由变量会与内层函数形成绑定关系,且是与闭包的具体实例关联,闭包的每个实例引用的自由变量互不干扰,这种互不干扰性我们再通过一个实例来了解下。

def outer():lst = []def inner(num):lst.append(num)print('lst:', lst)return inner
f1 = outer()
f2 = outer()f2(1)
f1(2)
f2(2)输出:
lst: [1]
lst: [2]
lst: [1, 2]

在这段代码中,通过f1 = outer(),f2 = outer(),我们生成两个闭包的实例,他们分别是f1,f2。

执行f2(1)时,执行闭包函数,向inner传递一个参数1,lst此时是空的,向其中添加一个元素1,所以此时lst的结果是[1]。

执行f1(1)时,执行闭包函数,向inner传递一个参数1,lst此时还是空的,向其中添加一个元素1,所以此时lst的结果是[1]。

执行f2(2)时,执行闭包函数,向inner传递一个参数2,因为已经执行了f2(1),lst已经是[1],再向其中添加一个元素2,所以此时lst的结果是[1,2]。

通过以上过程,我们可以验证并得出结论,lst在f1和f2中都存在,且他们之间互不干扰。

自由变量除了通过在outer外层函数中定义,还可以作为虚参,通过外层函数传入。请看下面的实例。

def outer(num1):def inner(num2):result = num1 + num2print('result:', result)return innerf1 = outer(1)
f1(2)输出:
result: 3

此时外层函数拥有一个虚参,在生成闭包实例时,需要相应向外层函数传入一个参数,这里传入参数值为1,被外层函数的接收后保存在num1中。在执行函数f1(2)时,等效于执行调用内部函数inner,因为inner也有一个参数,所以相应的在调用时也传入了一个参数,参数值为2,被内层函数的接收后保存在num2中。内层函数在执行时,num1是引用外层函数的num1,它的值为1,num2的值为2,相加后result的结果为3。最终输出result:3。

二、装饰器

1.装饰器基础

装饰器的本质就是对闭包的使用,它的作用是在不改变原有函数及函数名的基础上添加功能。

原则:1.不能修改被装饰的函数的源代码; 2.不能修改被装饰的函数的调用方式。

装饰器应用场景:

  • 引入日志
  • 函数执行时间统计
  • 执行函数前预备处理
  • 执行函数后清理功能
  • 权限校验等场景
  • 缓存

实例:我们有以下函数,通过添加一个装饰器,实现在原有输出内容的上下各添加一条分割线。

基础函数如下:

def f():print('---test---')
f()

我们采用闭包的方法来完成这个任务。

def wrapper(func):def inner():print("*****我是美丽分割线1*****")func()print("*****我是美丽分割线2*****")return inner
def f():print('---test---')
f = wrapper(f)
f()

我们看看使用装饰器是如何实现的:

def wrapper(func):print('正在装饰')def inner():print("*****我是美丽分割线1*****")func()print("*****我是美丽分割线2*****")return inner@wrapper
def f():print('---test---')
f()

我们看下它的执行流程:

(1)def wrapper(func),定义装饰器函数,并将其载入内存。

(2)def f(),定义函数。此时发现在它的上方有@wrapper,@wrapper是调用装饰器函数,@wrapper将f作为参数传递到wrapper中,该句等效于f= wrapper(f)。

(3)此时wrapper(func)会立即执行,输出“正在装饰”,然后将inner会返回给f,此时f = inner()

(4)f()是执行函数,实际执行的是inner(),因此会输出"*****我是美丽分割线1*****",func()是对f()本体的调用,此时会输出'---test---',最后输出"*****我是美丽分割线2*****"。

我们发现@wrapper是对f=wrapper(f)的一种简化。

在实际使用的时候,一个装饰器可以对多个函数进行装饰,只要在每个被装饰函数的上方增加一句调用装饰器函数。

import timedef timer(func):def deco():start_time = time.time()func()stop_time = time.time()print('the func run time is %s'%(stop_time-start_time))return deco@timer      #@timer 相当于就是test1 = timer(test1);此时test1=deco,deco里面的func=最开始的test1
def test1():time.sleep(3)print('in the test1')
@timer      #@timer 相当于就是test2 = timer(test2);此时test2=deco,deco里面的func=最开始的test2
def test2():time.sleep(5)print('in the test2')test1()
test2()

2.多个装饰器

多个装饰器可以应用在一个函数上,装饰的顺序自下而上,而调用的顺序是自上而下。

def wrapper_out1(func):print('wrapper_out1正在装饰')def inner1():print("这里是inner1-1")func()print("这里是inner1-2")return inner1def wrapper_out2(func):print('wrapper_out2正在装饰')def inner2():print("这里是inner2-1")func()print("这里是inner2-2")return inner2
@wrapper_out1
@wrapper_out2
def test():print("--test--")test()# 输出
wrapper_out2正在装饰
wrapper_out1正在装饰
这里是inner1-1
这里是inner2-1
--test--
这里是inner2-2
这里是inner1-2

执行过程:

(1)将wrapper_out1,wrapper_out2,test载入内存

(2)先对test进行装饰,因为test上面有两个装饰器,装饰按照自下而上原则,先执行@wrapper_out2,等效于test = wrapper_out2(test),输出'wrapper_out1正在装饰',然后将inner2传回给test;在执行@wrapper_out1,等效于test = wrapper_out1(test),注意此时括号里的test实际上是inner2,输出'wrapper_out1正在装饰',然后将inner1传回给test。这整个过程等效于:test = wrapper_out1(wrapper_out2(test)),括号里的test为原始test。

(3)调用test函数时,先执行inner1,输出:"这里是inner1-1",然后调用func(),这里的func实际是inner2,所以进入inner2,输出"这里是inner2-1",inner2中的func为test原函数,所以输出"--test--",然后输出"这里是inner2-2",inner2执行结束后,回到inner1中,输出"这里是inner1-2"。

3.对有参数和返回值的函数进行装饰

def func_dec(func):def wrapper(*args):print("average_value is %.2f."%(sum(args)/len(args)))result = func(*args)return resultreturn wrapper@func_dec
def add_sum(*args):return sum(args)args = range(10)
result = add_sum(*args)
print(result)# 输出
average_value is 4.50.
45

三、高级函数练习

1.装饰器基础练习

制作一个装饰器,实现在已有函数的字符串的上下各添加一段字符串,效果如下:

def test():print("这是第一次装饰器编程练习")
test()# 输出
Hello world
这是第一次装饰器编程练习
I love python

2.使用计时器装饰函数

制作一个装饰器,实现在已有函数基础上,输出每个函数的运行时长。

def test1():time.sleep(5)print("这是第一次装饰器编程练习")def test2():time.sleep(8)print("这是第二次装饰器编程练习")
test()

3.编写一个带参数的装饰器

def test(*args):print("这是第一次装饰器编程练习")
test('20010001','Tom')# 输出
Id is 20010001,Name is Tom
这是第一次装饰器编程练习

四、第十六课字典练习答案

1.字典基本练习

dic = {'python': 95,'html/css': 99,'c': 100
}'''
1.字典的长度是多少
2.请修改'html/css'这个key对应的value值为98
3.删除c这个key
4.增加一个key - value对,key值为php, value是90
5.获取所有的key值,存储在列表里
6.获取所有的value值,存储在列表里
7.判断javascript是否在字典中
8.获得字典里所有value的和
9.获取字典里最大的value
10.获取字典里最小的value
11.字典dic1 = {'php': 97}, 将dic1的数据更新到dic中
'''print(len(dic))
dic['html/css'] = 98
del dic['c']
dic['php'] = 90
lst_key = list(dic.keys())
lst_value = list(dic.values())
print('javascript' in dic)
print(sum(dic.values()))
print(max(dic.values()))
print(min(dic.values()))
dic1 = {'php': 97}
dic.update(dic1)

2.找到b值

字典D中有17个键值对,每个键值对以(a,b):v的方式存储,要求编写一段程序,找出满足a==10,v==1条件时b的值

D = {(4, 7): 0, (2, 6): 1, (10, 4): 1, (5, 11): 1, (4, 5): 1,
(2, 8): 0, (8, 11): 0, (10, 0): 1, (6, 11): 1, (9, 11): 1,
(1, 9): 0, (10, 1): 0, (7, 11): 1, (0, 9): 1, (3, 7): 1,
(10, 3): 1, (10, 2): 1}for a,v in D.items():if a[0] == 10 and v == 1:print(a[1])

3.词频统计

# 任务1:将所有大写转换为小写
words = words.lower()# 任务2:生成单词列表
words_list = words.split()# 任务3:生成词频统计
words_dict = {}
for word in words_list:if word in words_dict.keys():words_dict[word] += 1else:words_dict[word] = 1# 任务4:排序
words_dict_list = list(words_dict.items())
words_dict_list.sort(key = lambda x:x[1],reverse=True)
print(words_dict_list)
# 任务5:排除语法型词汇,代词、冠词、连词
exclude = ["the", "has", "of", "a", "for", "and","away","from","on","to","are","only","have","in","after"]for i in range(len(words_dict_list)-1,-1,-1):k = words_dict_list[i][0]if k in exclude:words_dict_list.pop(i)# 任务6:输出词频最大TOP20
print(words_dict_list[:20])

4.探寻身份证号

身份证号包含着许多信息,第7到14位是生日信息,倒数第二位是性别代码,男性为奇数,女性为偶数。要求将account列表中的三个元素按照info字典的格式输出。

account = ['Jone360403200105070312', 'Tom36040320020903043X', 'Susan360101200108110181']
info = {}
for x in account:name = x[:len(x) - 18]year = x[-12:-8]month = str(int(x[-8:-6]))day = str(int(x[-6:-4]))sex = lambda x:'女' if int(x[-2]) % 2 == 0 else '男'info[name] = dict(性别=sex(x), 生日='%s年%s月%s日' % (year, month, day))
print(info)

18.高级函数(闭包与装饰器)相关推荐

  1. 一木.溪桥学Python-10:函数闭包、装饰器、推导式

    一木.溪桥 在Logic Education跟Amy学Python 逻辑教育 :https://logicedu.ke.qq.com 12期:Python基础课 一木.溪桥学Python-10:函数闭 ...

  2. day20 函数闭包与装饰器

    装饰器:本质就是函数,功能是为其他函数添加新功能 原则: 1.不修改被装饰函数的源代码(开放封闭原则) 2.为被装饰函数添加新功能后,不修改被修饰函数的调用方式 装饰器的知识储备: 装饰器=高阶函数+ ...

  3. python高阶函数闭包装饰器_5.初识python装饰器 高阶函数+闭包+函数嵌套=装饰器...

    一.什么是装饰器? 实际上装饰器就是个函数,这个函数可以为其他函数提供附加的功能. 装饰器在给其他函数添加功能时,不会修改原函数的源代码,不会修改原函数的调用方式. 高阶函数+函数嵌套+闭包 = 装饰 ...

  4. py函数式编程(高阶函数map/reduce/filter/sorted、闭包函数/返回函数、匿名函数lamber、@装饰器decorator、偏函数functool.partial())

    #py函数式编程.py #高阶函数map/reduce/filter/sorted.闭包函数/返回函数.匿名函数lamber.@装饰器decorator.偏函数functool.partial()# ...

  5. Python基础知识——函数的基本使用、函数的参数、名称空间与作用域、函数对象与闭包、 装饰器、迭代器、生成器与yield、函数递归、面向过程与函数式(map、reduce、filter)

    文章目录 1 函数的基本使用 一 引入 二 定义函数 三 调用函数与函数返回值 2 函数的参数 一 形参与实参介绍 二 形参与实参的具体使用 2.1 位置参数 2.2 关键字参数 2.3 默认参数 2 ...

  6. python基础十 函数(下)匿名函数、高阶函数、闭包、装饰器

    目录 1. 匿名函数(lambda表达式) 2. 高阶函数 3. 函数的嵌套 4. nonlocal 关键字使用 5. 闭包 6. 装饰器 1. 匿名函数(lambda表达式) 简介 用一句话来表达只 ...

  7. Python基础-----列表生成式、偏函数、高阶函数、闭包、装饰器

    列表生成式 列表生成式(列表推导式):通俗理解使用for循环快速创建一个列表,最终要获取一个列表 下面这个我们经常使用比较麻烦的方法: my_list=[] for i in range(1,6):p ...

  8. python 如何判断一个函数执行完成_Python核心编程的四大神兽迭代器、生成器 、闭包以及装饰器...

    本文将主要分为4大部分,分别介绍Python核心编程中的迭代器.生成器 .闭包以及装饰器. 生成器 生成器是生成一个值的特殊函数,它具有这样的特点:第一次执行该函数时,先从头按顺序执行,在碰到yiel ...

  9. python函数装饰嵌套_python3--函数名本质,函数嵌套,闭包,装饰器

    python函数的嵌套和作用域链 函数的嵌套调用def max2(x,y): m = x if x > y else y  # 三元运算 return m def max4(a,b,c,d): ...

最新文章

  1. 【转】Linux入门命令篇(简训)
  2. 学习笔记(18):Python网络编程并发编程-守护进程
  3. (二)python3 只需3小时带你轻松入门——基本变量
  4. 小猿圈分享-数据分析工具
  5. TanDEM-X 90m DEM介绍与下载
  6. 钉钉机器人:python发送消息-加签模式
  7. Oracle搜索所有表查找关键字,根据关键字查询oracle中所有表的记录
  8. Unity3D动态加载FBX文件
  9. Dorado7自定义下拉框
  10. html爱心表白代码(最全)
  11. webgl框架介绍以及webgl项目的技术选型问题
  12. 离散数学12_第5章 关系与函数之等价关系与序关系、哈斯图
  13. Thingworx - 导航
  14. 关于手册的页码和有效页清单 - LEP
  15. 再谈中国的收入不平等问题
  16. Linux中断子系统---中断申请request_irq()与中断线程化request_threaded_irq()
  17. ucos通信邮箱的理解
  18. 7-301 sdut- C语言实验-数组逆序(数组移位)
  19. yum安装Redis教程
  20. 岭南师范学院计算机证书,2017年岭南师范学院全国计算机等级考试网上报考通知第48次网上预报名(自行打印带有相片的预报名表并签名)...

热门文章

  1. 告别“丝袜哥”,推荐这几个在线文档生成神器
  2. 解决CentOS-HTTP出现“httpd: Could not reliably determine the server‘s fully qualified domain name”问题
  3. Python学习教程系列
  4. 概念---数学分析2:常用概念汇总(完备性,柯西序列等...)
  5. 【编程游戏】贺岁霓虹灯。(参观109楼dh20156的霓虹灯)
  6. mysql主流使用版本_简述3个主流MySQL版本特性
  7. __builtin_expect的作用
  8. 2021最新秋招Java软件工程师面试笔试必备面试题及答案汇总
  9. Device eth0 has different MAC address than expected, ignoring
  10. 正弦向量内积求夹角c语言,获取两个向量a,b之间的夹角的几种方法