>>> numbers = [1, 2, 3, 5, 7]>>> letters = ['a', 'b', 'c']>>> z = zip(numbers, letters)>>> z<zip object at 0x7f112cc6ce48>>>> next(z)(1, 'a')Python 中的文件对象也是迭代器 。
>>> next(open('hello.txt'))'hello worldn'在 Python 标准库和第三方库中内置了大量的迭代器 。这些迭代器首先惰性迭代器一样,延迟工作直到你请求它们下一项 。
创建你自己的迭代器
知道你已经在使用迭代器是很有用的,但是我希望你也知道,你可以创建自己的迭代器和你自己的惰性迭代器 。
下面这个类构造了一个迭代器接受一个可迭代的数字,并在循环结束时提供每个数字的平方 。
class square_all:def __init__(self, numbers):self.numbers = iter(numbers)def __next__(self):return next(self.numbers) * 2def __iter__(self):return self但是在我们开始对该类的实例进行循环遍历之前,没有任何工作要做 。
这里,我们有一个无限长的可迭代对象 count,你可以看到 square_all 接受 count 而不用完全循环遍历这个无限长的迭代:
>>> from itertools import count>>> numbers = count(5)>>> squares = square_all(numbers)>>> next(squares)25>>> next(squares)36这个迭代器类是有效的,但我们通常不会这样做 。通常,当我们想要做一个定制的迭代器时,我们会生成一个生成器函数:
def square_all(numbers):for n in numbers:yield n**2这个生成器函数等价于我们上面所做的类,它的工作原理是一样的 。
这种 yield 语句似乎很神奇,但它非常强大:yield 允许我们在调用 next 函数之间暂停生成器函数 。yield 语句是将生成器函数与常规函数分离的东西 。
另一种实现相同迭代器的方法是使用生成器表达式 。
def square_all(numbers):return (n**2 for n in numbers)这和我们的生成器函数确实是一样的,但是它使用的语法看起来像是一个列表推导一样 。如果你需要在代码中使用惰性迭代,请考虑迭代器,并考虑使用生成器函数或生成器表达式 。
迭代器如何改进你的代码
一旦你已经接受了在代码中使用惰性迭代器的想法,你就会发现有很多可能来发现或创建辅助函数,以此来帮助你循环遍历和处理数据 。
惰性求和
这是一个 for 循环,它对 Django queryset 中的所有工作时间求和:
hours_worked = 0for event in events:if event.is_billable():hours_worked += event.duration下面是使用生成器表达式进行惰性评估的代码:
billable_times = (event.durationfor event in eventsif event.is_billable())hours_worked = sum(billable_times)请注意,我们代码的形状发生了巨大变化 。
将我们的计算工作时间变成一个惰性迭代器允许我们能够命名以前未命名(billable_times)的东西 。这也允许我们使用 sum 函数,我们以前不能使用 sum 函数是因为我们甚至没有一个可迭代对象传递给它 。迭代器允许你从根本上改变你组织代码的方式 。
惰性和打破循环
这段代码打印出日志文件的前 10 行:
for i, line in enumerate(log_file):if i >= 10:breakprint(line)这段代码做了同样的事情,但是我们使用的是 itertools.islice 函数来惰性地抓取文件中的前 10 行:
from itertools import islicefirst_ten_lines = islice(log_file, 10)for line in first_ten_lines:print(line)我们定义的 first_ten_lines 变量是迭代器,同样,使用迭代器允许我们给以前未命名的东西命名(first_ten_lines) 。命名事物可以使我们的代码更具描述性,更具可读性 。
作为奖励,我们还消除了在循环中使用 break 语句的需要,因为 islice 实用函数为我们处理了中断 。
你可以在标准库中的 itertools 中找到更多的迭代辅助函数,以及诸如 boltons 和 more-itertools 之类的第三方库 。
创建自己的迭代辅助函数
你可以在标准库和第三方库中找到用于循环的辅助函数,但你也可以自己创建!
这段代码列出了序列中连续值之间的差值列表 。
current = readings[0]for next_item in readings[1:]:differences.append(next_item - current)current = next_item请注意,这段代码中有一个额外的变量,我们每次循环时都要指定它 。还要注意,这段代码只适用于我们可以切片的东西,比如序列 。如果 readings 是一个生成器,一个 zip 对象或其他任何类型的迭代器,那么这段代码就会失败 。
让我们编写一个辅助函数来修复代码 。
这是一个生成器函数,它为给定的迭代中的每个项目提供了当前项和下一项:
def with_next(iterable):"""Yield (current, next_item) tuples for each item in iterable."""iterator = iter(iterable)current = next(iterator)for next_item in iterator:yield current, next_itemcurrent = next_item
推荐阅读
- 茶叶市场营销策略中的茶文化营销
- 苏麻喇姑嫁给康熙皇帝了吗 康熙王朝中的苏麻喇姑嫁给康熙了吗
- python多线程爬取youtube视频,外面的世界很精彩
- python爬取拉勾网数据并进行数据可视化
- 淮阴侯列传中的韩信是一个怎样的人
- Python匿名函数的介绍及用途
- Python教程:使用Turtles画出带有花瓣的花
- 9道国宴中的巅峰中国菜 中国国宴菜单
- 对人伸出中指,其中的意思 竖中指是什么意思
- iOS 14中的5个隐藏功能