1. 利用闭包去替换简单的类
有的时候,你定义了一个类,但是这个类除了 __init__() 方法外,只有一个方法。
这样的话,定义一个类显得有点不够优雅了,首先类肯定比函数要占用更多的内存,并且也复杂。
下面看我是如何用闭包优雅且简洁的解决这样的问题的。
首先假设你定义了一个这样的类:
class ShowInfo:
def __init__(self, name, age):
self.name = name
self.age = age
def show_format(self, template):
print(template.format_map(vars(self)))
obj = ShowInfo('a', 1)
tpl = """姓名: {name}
年龄: {age}
"""
obj.show_format(tpl)
# 以下是打印出来的结果
"""
姓名:shark
年龄:18
"""
这个类是很无聊的,就是把对象的属性按照用户输入的格式,打印出来。随然整个类没什么卵用,但是足可以说明我们要阐述的内容。
以上的类可以使用闭包来优雅的实现:
def show_info(name, age):
name=name
age = age
info = locals()
def show_format(template):
print(template.format_map(info))
return show_format
show = show_info('shark', 18)
tpl = """姓名: {name}
年龄: {age}
"""
show(tpl)
2. 回调函数
假设你写了一个函数需要用到调函数,原因可能是执行了异步操作,或者在一些框架中会要求有回调函数。
如下:
def apply_async(func, args, *, callback):
# 计算结果
result = func(*args)
# 接着在这个函数内部打印计算的结果
callback(result)
# 用于计算的函数
def add(x, y):
return x + y
# 回调函数
def print_result(result):
print('Got:', result)
# 使用
apply_async(add, (3, 5), callback=print_result)
# Got: 8
看起来上面的代码都很美好,但是假如回调函数想用到额外的变量,就会很麻烦,因为我们这里把回调函数的参数写死了,只能接收一个参数。
解决办法: 可以用闭包来实现。
比如下面的闭包,就是在闭包中使用一个变量来记录总共处理了多少个结果了。
下面的函数实现的功能就是,保存一个内部序列号,每次接收到一个 result 的时候序列号 加 1。
def make_handler():
sequence = 0
def handler(result):
# 声明一下这个不是此函数的本地变量,就是改变作用域的关系
nonlocal sequence
sequence += 1
print('[{}] Got: {}'.format(sequence, result))
return handler
# 获取到回调函数
handler = make_handler()
# 使用这个回调函数
apply_async(add, ('hello', 'world'), callback=handler) # [1] Got: helloworld
apply_async(add, ('hello', 'world'), callback=handler) # [2] Got: helloworld
更高级的办法是使用协程
def make_handler():
sequence = 0
while True:
# 使用 yield 关键字来实现协程
result = yield
sequence += 1
print('[{}] Got: {}'.format(sequence, result))
使用的时候就需要用协程的 send() 方法了。
handler = make_handler()
r = next(handler) # 先释放 yield 的返回值
apply_async(add, (3, 8), callback=handler.send)
apply_async(add, ('hello', 'world'), callback=handler.send)
你可以看到输出的效果是一样的。