惰性处理(lazy evaluation)是一种有效编程的关键概念,只有在必要时才会计算值。历史上,在Python中,所有主要的内置数据结构(列表、元组)都是“贪婪”的:它们会提前创建并在内存中放置所有元素。随着数据量和流处理任务的增长,出现了惰性计算的需求。
问题: 贪婪计算在可以逐步获得结果的地方会导致内存和时间的低效使用,例如在过滤、转换长集合或流式文件时。
解决方案: Python中出现了许多惰性计算的工具:生成器、迭代器,以及标准库中的函数(map、filter、zip、enumerate)和模块itertools。它们都返回“惰性”对象,这些对象一次提供一个值。
代码示例:
result = map(lambda x: x * x, range(100)) # 返回生成器迭代器 for y in result: print(y) # 值在迭代时计算 import itertools inf = itertools.count(1) for i in inf: if i > 3: break print(i) # 1, 2, 3
关键特征:
Python3中的map/filter函数总是返回列表吗?
不,在Python 3中,这些函数返回迭代器,而不是列表。要获得列表,需要将结果包装在list()中。
x = map(int, ['1', '2']) # <map object> list(x) # [1, 2]
可以在不转换为列表的情况下获取map结果的长度吗?
不,迭代器在遍历所有元素之前不知道里面的元素数量。需要通过list()计算,这会取消惰性。
Python3中的range函数是贪婪的还是惰性的?
惰性:range创建“range对象”——它在请求时“计算”元素,而不存储整个序列。
脚本处理一个巨大的CSV文件,通过list(open(f))创建所有行的列表。服务器在大文件处理时因为内存不足而“崩溃”。
优点:
缺点:
代码使用惰性处理:通过迭代器for line in open(f)逐行处理文件,或者通过map/filter处理而不创建中间集合。
优点:
缺点: