python3 运维开发

Python3 函数的更高级的话题(二)

2018-11-25  本文已影响22人  运维开发_西瓜甜

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)

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

上一篇 下一篇

猜你喜欢

热点阅读