讲闭包之前,我们需要模拟一个简单的场景,用不同的形式实现。
场景:准备好人民币,去银行换美元 或者 日元。
1. 用函数实现
rate_usa = 0.7 # 美元汇率
rate_jp = 100 # 日元汇率
def ret_money(rate, money):
print(rate * money) # 汇率 * 钱数 就是我们可以换得了多少
ret_money(rate_usa, 1000) # 1000元 换 美元
ret_money(rate_usa, 2000) # 2000元 换 美元
ret_money(rate_jp, 100) # 100元 换 日元
ret_money(rate_jp, 200) # 200元 换 日元
这是我想到的最简单的方法来实现,那么发现一个问题,每次我要计算的时候,都需要去传汇率和要换的钱数,如果要计算100次、1000次,那一定疯掉,所以我需要其他更简单的方法实现。。。。
2. 面向对象实现:
rate_usa = 0.7
rate_jp = 100
class RetMoney(object):
def __init__(self, rate): # rate=汇率
self.rate = rate
def result(self, money): # money=要换的钱数
print(self.rate * money)
ret_money = RetMoney(rate_usa) # 创建实例 传入汇率
ret_money.result(1000) # 调用计算方法,传入要换的钱数
ret_money.result(2000)
ret_money2 = RetMoney(rate_jp)
ret_money.result(100)
ret_money.result(100)
面向对象方法实现,很明显,汇率我只穿一次,之后我只改要换的钱数,方便了很多,但是似乎还是有些浪费资源,是不是还有更好的方法呢?闭包要登场了。。。。
3. 闭包实现:
rate_usa = 0.7
rate_jp = 100
def ret_money(rate):
def result(money):
print(rate * money)
return result
result_usa = ret_money(rate_usa)
result_jp = ret_money(rate_jp)
result_usa(1000)
result_usa(2000)
result_jp(100)
result_jp(200)
那么这种形式,就是闭包
闭包的格式:
函数嵌套,
内部的函数需要用到外部的变量,
外部函数返回内部函数的引用,
外部函数必须有一个参数
尽管列出了闭包的格式,初次见到闭包的人也一定会很懵
先看一段代码:
def a():
print("11111")
a()
很显然 输入“11111”
那么改一下
def a():
print("11111")
print(a)
输出了一个对象,<function a at 0x103860e18>
,似乎告诉这个函数位于一个地址
其实当程序执行时,看到def 发现是要定义一个函数,那么就会创建一个变量 a 再将函数体放在一个内存地址,并让a指向这个地址—-如下图:
那么闭包其实就是一种特殊的函数利用函数引用来实现
创建一个新的变量,让它指向闭包中,内部函数的引用,
给这个新变量加上一对括号,就相当于执行了闭包中的内部函数。
你可能会想为什么这么麻烦来解决,直接用一个函数,定义几个全局变量不就可以解决吗?话粗理不粗,确实有几分道理,那接下来用闭包来实现另外一个场景。。。。
场景:我有一个已经写好的函数,实现了转账的功能,突然你的领导说让你在不改变写好的函数代码结构及结果的情况下,添加一个身份验证的功能,该如何修改?闭包就派上用场了,下面我们来实现一下
伪代码: 在不修改这个函数的结构及结果的情况,添加额外的身份验证功能
def test():
print("转账功能...")
# 闭包实现 添加身份验证功能
def set_fun(func):
def call_fun():
print("身份验证")
return func()
return call_fun
# 转账功能
def test():
print("转账功能")
test = set_fun(test)
test()
初次接触闭包的人,看上一个场景实现的闭包可能还能看得懂,这个可能会有些懵,可以看下面的图片,具体的分析的这段代码的执行过程(由于软件没办法写那么多,分成了两张图):
上面的图片比较详细的分析了整个过程,
那么这种利用闭包的特性,在不改动原有代码的情况,为一个函数添加额外的功能,叫做装饰器。那么真正的装饰器有特定的写法。
def set_fun(func):
def call_fun():
print("身份验证")
func()
return call_fun
@set_fun # 这行代码相当于 test = set_fun(test)
def test():
print("转账功能")
# test = set_fun(test) 注释这行代码
test()
在需要添加额外功能的函数上面,写@+闭包外层函数的函数引用,这就是一个装饰器,闭包大部分都是用来完成装饰器而使用的。
@set_fun 其实是python的语法糖,告诉程序员这是一个装饰器。
装饰器是什么:@闭包的外部函数引用
装饰器作用:装饰我们的函数,给函数添加额外的功能,不改变装饰前的函数代码(道德)
代码怎么写:
#1.先写闭包
#2.在要装饰的函数上写闭外外层的引用
后续还会写一篇根据要装饰的函数的不同情况(有参无参有返回无返回—到万能)需要如何修改的文章 - 。-iiii