如果我有两个不同长度的迭代,我怎样才能最干净地对它们进行配对,重新使用较短值的值直到消耗掉更长的值?
例如,给出两个列表
l1 = ['a', 'b', 'c']
l2 = ['x', 'y']
最好让函数fn()
成对:
>>> fn(l1, l2)
[('a', 'x'), ('b', 'y'), ('c', 'x')]
我发现我可以编写一个函数来执行此操作
def fn(l1, l2):
if len(l1) > len(l2):
return [(v, l2[i % len(l2)]) for i, v in enumerate(l1)]
return [(l1[i % len(l1)], v) for i, v in enumerate(l2)]
>>> fn(l1, l2)
[('a', 'x'), ('b', 'y'), ('c', 'x')]
>>> l2 = ['x', 'y', 'z', 'w']
>>> fn(l1,l2)
[('a', 'x'), ('b', 'y'), ('c', 'z'), ('a', 'w')]
然而,我贪婪并且好奇还有其他什么方法存在?所以我可以选择最明显和优雅的,并且对其他方法保持警惕。
许多类似问题中提出的 itertools.zip_longest
非常接近我想要的用例,因为它有一个fillvalue
参数,用于填充较长的对。但是,这只需要一个值,而不是回到较短列表中的第一个值。
作为一个注释:在我的用例中,一个列表总是比另一个列表短得多,这可能允许快捷方式,但通用解决方案也会令人兴奋!
答案 0 :(得分:1)
您可以将itertools.cycle()
与zip
一起使用以获得所需的行为。
正如itertools.cycle()
文件所说:
使迭代器从iterable返回元素并保存每个元素的副本。当iterable耗尽时,返回保存副本中的元素。
例如:
>>> l1 = ['a', 'b', 'c']
>>> l2 = ['x', 'y']
>>> from itertools import cycle
>>> zip(l1, cycle(l2))
[('a', 'x'), ('b', 'y'), ('c', 'x')]
因为在您的情况下,l1
和l2
的长度可能会有所不同,您的通用fn()
应该是这样的:
from itertools import cycle
def fn(l1, l2):
return zip(l1, cycle(l2)) if len(l1) > len(l2) else zip(cycle(l1), l2)
示例运行:
>>> l1 = ['a', 'b', 'c']
>>> l2 = ['x', 'y']
# when second parameter is shorter
>>> fn(l1, l2)
[('a', 'x'), ('b', 'y'), ('c', 'x')]
# when first parameter is shorter
>>> fn(l2, l1)
[('x', 'a'), ('y', 'b'), ('x', 'c')]
答案 1 :(得分:-1)
如果您不确定哪一个是最短的,next
it.cycle
两个列表中最长的len
:
def fn(l1, l2):
return (next(zip(itertools.cycle(l1), itertoools.cycle(l2))) for _ in range(max((len(l1), len(l2)))))
>>> list(fn(l1, l2))
[('a', 'x'), ('a', 'x'), ('a', 'x')]
itertools.cycle
将无限重复此列表。然后,zip
两个无限列表一起得到你想要的循环,但无限重复。所以现在,我们需要将其修剪到合适的尺寸。 max((len(l1), len(l2)))
将找到两个列表中最长的长度,然后next
无限可迭代,直到达到正确的长度。请注意,这会返回一个生成器,因此要获取您想要的输出,请使用list
来使用该函数。