在列表推导式中使用append修改列表

5

注意:这是一个纯粹的代码高尔夫问题,所以我知道我的要求在生产中是不好的实践

我试图在列表推导式中修改一个数组,但出现了卡顿,我不知道为什么或如何修复它。

我正在处理一个深度未定的列表列表,并需要将它们压缩成一个平面列表 - 对于那些好奇的人,它是这个问题。但在这个阶段,让我们只说我需要一个列表中所有元素的平面列表,如果它是一个列表,则为0。

正常的方法是遍历列表,如果它是一个列表,则将其添加到末尾,如下所示:

for o in x:
 if type(o)==type([]):x+=o
 else:i+=o
print i

我正在尝试使用列表推导式来缩短这个代码,像这样。
print sum([
 [o,x.append(o) or 0][type(o)==type([])]
 for o in x
]))

现在,我知道List.append返回None,所以为了确保我得到一个数字值,惰性求值说我可以做x.append(o) or 0,而且由于None是“falsy”,它将评估第二部分,值是0
但它并没有。如果我把x.append()放到对x的列表推导中,它不会崩溃或出错,也不会返回迭代错误,它只会冻结。为什么append在列表推导中冻结,但上面的for循环正常工作?

这个问题有些离题,但对于原始问题:将嵌套的可迭代对象展平为迭代器不是更简单吗?(在3.3+中可以用两行代码实现,如果不能使用第三方模块如“more-itertools”,则需要3行代码),然后只需在该迭代器上编写简单的代码即可。 - abarnert
@abarnert from itertools import* 这段代码有22个字符,然后你需要调用实际的方法。这对于仅仅使用一个库来说是相当大的代价。 - user764357
是的,但它在more-itertools中,这就是为什么我在原始评论中说“像more-itertools这样的第三方模块”。(我相信它被称为collapse而不是flatten,不幸的是这增加了1个字符...) - abarnert
是的,那么你需要像 def f(x): try: yield from map(f, x) except: yield x 这样的东西,它更长,并需要 Python 3.3+。 - abarnert
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/42905/discussion-between-lego-stormtroopr-and-abarnert - user764357
显示剩余3条评论
2个回答

8
or可能比较惰性,但列表定义不是。对于x中的每个o,当评估[o,x.append(o) or 0][type(o)==type([])]这个怪物时,Python必须评估[o,x.append(o) or 0],这意味着评估x.append(o) or 0,这意味着无论o是否为列表,o都将被添加到x中。因此,你最终会得到将x中的每个元素都附加到x中的新列表,然后它们会一遍又一遍地被附加,直到出现OutOfMemoryError

我可能没有等待足够长的时间才能看到错误。我猜只能回到起点了 :( - user764357
1
@LegoStormtroopr:如果你使用64位Python,在一个可以根据需要扩展交换空间的平台上(像OS X和Windows默认情况下都是这样),错误可能要等到经过数小时的交换空间抖动后才会出现... - abarnert

3

怎么样:

y = [element for element in x if type(element) != list or x.extend(element)]

(请注意,extend会平铺列表,而append只会将嵌套列表添加回末尾,未经展开)。

2
为什么不直接写 type(element) != list 呢?list 就是 [] 的类型。 - mgilson
啊,好的!我正在修改@Lego的代码,结果有点走神了 :) 谢谢!已经改正了! - Roberto
高尔夫球打得很棒,但它并没有真正回答问题。虽然我不能在良心上把这个作为我的解决方案提交给挑战,并建议您不要这样做。 - user764357
完全没有出错 :) - user764357

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接