python - 流生成器。详细使用迭代器
问题描述
我试图弄清楚迭代器如何与这个例子一起工作:
有一个函数为给定的可迭代对象(列表、生成器等)生成流生成器,其元素包含位置和值并按外观顺序排序。流生成器等于初始流(没有位置),间隙用零填充。
from itertools import count
def gen_stream(total, sorted_iterable, extractor=lambda x: x):
sorted_iterator = iter(sorted_iterable)
iterable = count() if total is None else range(total)
try:
current_extracted_record = extractor(next(sorted_iterator))
except StopIteration:
current_extracted_record = None
for i in iterable:
if current_extracted_record:
if i == current_extracted_record[0]:
try:
yield current_extracted_record[1]
current_extracted_record = extractor(next(sorted_iterator))
except StopIteration:
current_extracted_record = None
else:
yield 0
else:
yield 0
例如:
gen = gen_stream(9,[(4,111),(7,12)])
list(gen)
[0, 0, 0, 0, 111, 0, 0, 12, 0] # first element has zero index, so 111 located on fifth position, 12 located on 8th position
此功能还支持自定义位置值提取器,用于更高级的情况,例如
def day_extractor(x):
months = [31, 28, 31, 30, 31, 31, 30, 31, 30, 31, 30, 31]
acc = sum(months[:x[1] - 1]) + x[0] - 1
return acc, x[2]
precipitation_days = [(3,1,4),(5,2,6)]
list(gen_stream(59,precipitation_days,day_extractor)) #59: January and February to limit output
[0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
precipitation_days 格式如下:(d,m,mm),其中 d - 月中的天数,m - 月,mm - 以毫米为单位的降水量因此,例如:
(3,1,4) # January,3 precipitation: 4 mm
(5,2,6) # February,5 precipitation: 6 mm
提取器作为带有默认值的可选第三个参数传递 - 处理(位置,值)对的 lambda 函数,如第一个示例中所示。
这是问题开始的地方
问题1 我可以更换吗
try:
current_extracted_record = extractor(next(sorted_iterator))
except StopIteration:
current_extracted_record = None
使用函数next的默认值,而不是通过使用一行代码来捕获异常 StopIteration
current_extracted_record = extractor(next((sorted_iterator), None))
它在其他情况下总是能正常工作吗?
问题 2如何通过使用next()方法的默认值和循环而不是循环for来替换这部分。理论上,代码应该更短。
for i in iterable:
if current_extracted_record:
if i == current_extracted_record[0]:
try:
yield current_extracted_record[1]
current_extracted_record = extractor(next(sorted_iterator))
except StopIteration:
current_extracted_record = None
else:
yield 0
else:
yield 0
问题 3这似乎是一个愚蠢的问题,但据我了解,提取器没有索引。那么方括号中的数字是什么意思呢?
current_extracted_record[0]
current_extracted_record[1]
谢谢,如果你能帮忙。
对于线程中的 3 个问题,我深表歉意,但在我看来,它们以不同的细节描述了相同的问题。
答案(问题1&问题2)
def gen_stream(total, sorted_iterable, extractor=lambda x: x):
elem_iter = iter(map(extractor, sorted_iterable))
pos, val = next(elem_iter, (None, None))
cnt = 0
while total is None or cnt < total:
if cnt == pos:
yield val
pos, val = next(elem_iter, (None, None))
else:
yield 0
cnt += 1
解决方案
我会将整个功能减少到
from itertools import count, islice, chain, repeat
def gen_stream(sorted_iterable, *, extractor=lambda x: x, total=None):
itr = chain(map(extractor, sorted_iterable), repeat(None))
current = next(itr)
for i in islice(count(), total):
if current is None or i != current[0]:
yield 0
else:
yield current[1]
current = next(itr)
我们创建一个迭代器,其元素是extractor
应用于原始迭代的返回值,然后根据None
需要持续。无需StopIteration
明确检查。
如果total
是None
,则islice(count(), total)
等价于count()
; 否则,它等价于range(total)
。
或者,
def gen_stream(sorted_iterable, *, extractor=lambda x: x, total=None):
def stream():
pos = 0
for new_pos, value in map(extractor, sorted_iterable):
yield from repeat(0, new_pos - pos)
yield value
pos = new_pos + 1
yield from islice(chain(stream(), repeat(0)), total)
内部生成器将用 0 修补间隙,然后最终yield from
生成所需的流。
在每种情况下,我都将两个可选参数设为仅关键字,因为我一直在输入gen_stream([...], 9)
,不小心设置了提取器而不是总数。如果您愿意,您可以保留原始签名或其他变体。
推荐阅读
- laravel - 无法在 Laravel 中捕获数据库异常
- python - 在 AWS 中保存(部署)后未发布 Python Lambda 函数
- python - 使用 pandas 将 Julian 转换为日历日期
- arrays - 涉及多个选项卡的 Google 表格查询中的 ARRAY_LITERAL 错误
- php - 如何在 laravel 中使用 neo4j-php-client 格式化 chyper 查询的结果
- c++ - 将对象分配给 C++ 类构造函数中的成员变量
- python-3.x - 如何检查特定输入?
- image - 如果单元格具有值,则将具有透明背景的 png 图像应用于行
- html - 使用 Flask-WTF/WTForms 在 Flask 中添加取消按钮
- java - 如何使用java API查询那些数组类型字段包含给定数组的至少一个元素的所有实体?