闭包
Posted Python3技术笔记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了闭包相关的知识,希望对你有一定的参考价值。
闭包
引入
想想看怎样用程序实现下面的功能呢?
在一个聊天软件中显示是谁发送了这条信息,即:一条信息标记了是谁发送的
今天我们要研究的知识点是闭包
,实现上述功能的方式可能有多种,但是闭包会更简单。
问题解决
普通方式
def say(user_name, content):
print("(%s):%s" % (user_name, content))
user_name1 = "安娜"
user_name2 = "双双"
say(user_name1, "今天吃了么?")
say(user_name2, "吃了~")
say(user_name1, "吃了啥?")
say(user_name2, "半只牛~")
say(user_name1, "为啥不给我吃?")
say(user_name2, "我一个人刚刚好~~")
say(user_name1, "友谊的小船说翻就翻!")
say(user_name2, "我会游泳~~~")
运行效果如下:
(安娜):今天吃了么?
(双双):吃了~
(安娜):吃了啥?
(双双):半只牛~
(安娜):为啥不给我吃?
(双双):我一个人刚刚好~~
(安娜):友谊的小船说翻就翻!
(双双):我会游泳~~~
小总结:
- 上述代码已经实现了要求,但是每次发送信息时需要将用户名称传递到
say
函数中,相对比较麻烦
面向对象的方式解决上述问题
class Person(object):
def __init__(self, name):
self.user_name = name
def say(self, content):
print("(%s):%s" % (self.user_name, content))
p1 = Person("安娜")
p2 = Person("双双")
p1.say("今天吃了么?")
p2.say("吃了~")
p1.say("吃了啥?")
p2.say("半只牛~")
p1.say("为啥不给我吃?")
p2.say("我一个人刚刚好~~")
p1.say("友谊的小船说翻就翻!")
p2.say("我会游泳~~~")
运行结果:
(安娜):今天吃了么?
(双双):吃了~
(安娜):吃了啥?
(双双):半只牛~
(安娜):为啥不给我吃?
(双双):我一个人刚刚好~~
(安娜):友谊的小船说翻就翻!
(双双):我会游泳~~~
小总结:
- 通过面向对象的方式能够实现上述要求,但是发现使用了类以及对象,总体感觉还是较为复杂,再者说继承的
object
类中有很多默认的方法,既然这个程序不需要,显然会造成一定的浪费
使用闭包解决上述问题
def person(name):
def say(content):
print("(%s):%s" % (name, content))
return say
p1 = person("安娜")
p2 = person("双双")
p1("今天吃了么?")
p2("吃了~")
p1("吃了啥?")
p2("半只牛~")
p1("为啥不给我吃?")
p2("我一个人刚刚好~~")
p1("友谊的小船说翻就翻!")
p2("我会游泳~~~")
函数引用
# 定义函数可以理解为:
# 定义了一个全局变量,其变量名字是函数的名字,即test
# 这个test变量指向了一个代码块,这个代码块是函数
# 其实就是说test保存了一个代码块的地址,即引用
def test():
print("--- in test func----")
test() # 这是调用函数
ret = test # 用另外一个变量 复制了 test这个引用,导致ret变量也指向那个 函数代码块
# 下面输出的2个地址信息是相同的
print(id(ret))
print(id(test))
# 通过引用调用函数
ret()
运行结果:
--- in test func----
140212571149040
140212571149040
--- in test func----
闭包的概念
闭包(closure) 定义非常抽象,很难看懂
下面尝试从概念上去理解一下闭包:
- 在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。 —— 维基百科
https://zh.wikipedia.org/wiki/闭包_(计算机科学)
用比较容易懂的人话说:就是当某个函数被当成对象返回时,夹带了外部变量,就形成了一个闭包。可以这样理解,闭包就是能够读取其他函数内部变量的函数
看如下案例,便于理解什么是闭包:
def make_printer(msg): # 可以认为是 外部函数
def printer(): # 可以认为是 内部函数
print(msg)
return printer # 返回的内部函数的引用
printer = make_printer(\'Good!\')
printer()
运行结果:
Good
闭包案例
代码示例:
def test(number):
def test_in(number_in):
print("in test_in 函数, number_in is %d" % number_in)
return number + number_in
return test_in
# 给test函数赋值,这个20就是给参数number
ret = test(20)
# 注意这里的100其实给参数number_in
print(ret(100))
# 注意这里的200其实给参数number_in
print(ret(200))
运行结果:
in test_in 函数, number_in is 100
120
in test_in 函数, number_in is 200
220
使用闭包需要注意的问题
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。因此可以手动解除对匿名函数的引用,以便释放内存。
使用闭包修改外部函数中的变量
def counter(start=0):
def add_one():
nonlocal start # nonlocal 关键字用于在嵌套函数内部使用变量,其中变量不应属于内部函数。
start += 1
return start
return add_one
c1 = counter(5) # 创建一个闭包
print(c1())
print(c1())
c2 = counter(50) # 创建另外一个闭包
print(c2())
print(c2())
print(c1())
print(c1())
print(c2())
print(c2())
运行结果:
6
7
51
52
8
9
53
54
多个闭包
如上面的代码中,调用了2次counter
,也就意味着创建了2个闭包,并且每个闭包之间没有任何关系。
大家是否有种感觉,好像闭包与对象有些类似。确实是这样的,对象其实可通俗的理解为数据(属性) + 功能(方法),而闭包也可以理解为数据 + 功能,只不过此时数据是外部函数中的那些局部变量或者形参,而功能则是内部函数。对象适合完成较为复杂的功能,而闭包则更轻量
闭包总结
- 闭包定义是在函数内再嵌套函数
- 闭包是可以访问另一个函数局部作用域中变量的函数
- 闭包可以读取另外一个函数内部的变量
- 闭包可以让参数和变量不会被垃圾回收机制回收,始终保持在内存中(而普通的函数调用结束后 会被Python解释器自动释放局部变量)
以上是关于闭包的主要内容,如果未能解决你的问题,请参考以下文章
Groovy闭包 Closure ( 自定义闭包参数 | 自定义单个闭包参数 | 自定义多个闭包参数 | 闭包参数默认值指定 )
Groovy闭包 Closure ( 闭包定义 | 闭包类型 | 查看编译后的字节码文件中的闭包类型变量 )
iOS开发-Swift进阶之闭包,逃逸闭包 & 非逃逸闭包!
Groovy闭包 Closure ( 闭包调用 | 闭包默认参数 it | 代码示例 )