以下是一个示例:
def renormalize(cont):
'''
each value from the original container is scaled by the same factor
such that their total becomes 1.0
'''
total = sum(cont)
for v in cont:
yield v/total
list(renormalize(range(5))) # [0.0, 0.1, 0.2, 0.3, 0.4]
list(renormalize(k for k in range(5))) # [] - a bug!
显然,当renormalize
函数接收到一个生成器表达式时,它不能按预期工作。它假设可以多次迭代容器,而生成器只允许一次遍历。
理想情况下,我希望能够这样做:
def renormalize(cont):
if not is_container(cont):
raise ContainerExpectedException
# ...
如何实现
is_container
?我想我可以在第二次遍历开始时检查参数是否为空。但是对于更复杂的函数,不明确什么时候开始第二次遍历,这种方法就行不通了。此外,我宁愿在函数入口处进行验证,而不是在函数内部深处进行验证(并且每当修改函数时都要移动它)。
当然,我可以重写
renormalize
函数以正确使用单遍迭代器。但这需要将输入数据复制到容器中。复制数百万个大列表“以防万一它们不是列表”的性能影响是荒谬的。编辑:我的原始示例使用了一个
weighted_average
函数:def weighted_average(c):
'''
returns weighted average of a container c
c contains values and weights in tuples
weights don't need to sum up 1 (automatically renormalized)
'''
return sum((v * w for v, w in c)) / sum((w for v, w in c))
weighted_average([(0,1), (1,1)]) #0.5
weighted_average([(k, 1) for k in range(2)]) #0.5
weighted_average((k, 1) for k in range(2)) #mistake
但这并不是最好的例子,因为重写为使用单次遍历的weighted_average
版本可能更好:
def weighted_average(it):
'''
returns weighted average of an iterator it
it yields values and weights in tuples
weights don't need to sum up 1 (automatically renormalized)
'''
total_value = 0
total_weight = 0
for v, w in it:
total_value += v
total_weight += w
return total_value / total_weight
itertools.tee()
来保证您可以无条件地迭代任意次数。如何在设计算法时不明确这一点呢? - S.Lottitertools.tee
问题苦恼过,试图找到一种方法使其对需要通过可迭代对象进行两次遍历的算法“不可见”。对我来说,这从未是模糊或神秘的,只是需要小心确保迭代器被正确地“tee-d”。我非常好奇它如何通过一个不是“完全明显”的循环变得更加复杂。感谢您考虑了这个问题,而不是像有些人那样坚持认为这是一个要求(当它不是)或者说“他们只是好奇。”好奇心并不能让一个不好的问题变得好。 - S.Lott