问题描述
我试图弄清楚为什么嵌套列表理解无法正常工作。代码:
def myzip(*args):
try:
x = [iter(i) for i in [*args]]
while True:
# for i in range(5):
yield [next(ii) for ii in x]
# I want to kNow why the commented line below won't work
# yield [next(ii) for ii in [iter(i) for i in [*args]]]
# or why this outputs [1,'a']
# return [next(ii) for ii in [iter(i) for i in [*args]]]
except:
pass
def main():
print(list(myzip([1,2,3,4,5],['a','b','c'])))
if __name__ == '__main__':
main()
如果我将[iter(i) for i in [*args]]
分配给x
,然后使用此列表理解[next(ii) for ii in x]
,一切正常。输出为:
[[1,'a'],[2,'b'],[3,'c']]
但是,如果我尝试忽略变量x
,并在一个更大的列表理解(注释行)[next(ii) for ii in [iter(i) for i in [*args]]]
中进行操作,它将陷入无限循环。如果将无限循环替换为for循环(带注释的行),则输出为:
[[1,[1,'a']]
此外,如果我尝试return [next(ii) for ii in [iter(i) for i in [*args]]]
,它只会返回:
[1,'a']
有人可以告诉我为什么吗?
解决方法
所以我不会直接回答您的问题(因为评论已经很好地涵盖了这个问题),但是我将展示如何将它作为可怕的衬纸来完成好玩:
def my_zip(*args):
yield from iter(lambda data=[iter(a) for a in args]: [next(d) for d in data],None)
共有三个部分。
- 在
iter
之后,yield from
的首次使用将创建callable_iterator
。这将重复调用其第一个参数(lambda
),直到它看到其第二个参数(哨兵值,在这种情况下为None
),或者它得到StopIteration
错误遇到 any 异常。 (在这种情况下,将是StopIteration
例外。 - 在
lambda
中,我有一个data
的默认值,它是您要迭代的迭代器的列表。该函数仅创建一次,因此以后每次对该函数的调用都将引用此函数。 - 最后,在
lambda
的主体中,我每个函数调用手动在data
中推进每个迭代器一次。这将一直持续到迭代器之一用尽为止,此时将引发StopIteration
。如您所见,在这种情况下,哨兵值并不重要,因为第一个iter
调用一旦获得StopIteration
就会保释,这会冒泡list
消费者,导致它停止迭代。由于存在异常,因此哨兵值无关紧要。
最后:请永远不要真正用真实的代码来做。
编辑:一些示例输出
In [90]: list(my_zip('abcd',[1,2,3]))
Out[90]: [['a',1],['b',2],['c',3]]
In [91]: list(my_zip('abcd',3,4,5,6]))
Out[91]: [['a',3],['d',4]]
说明编辑:在下面向他们推荐@superb Rain的聪明话。