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)

你可以看到输出的效果是一样的。