Python - 单行嵌套for循环

4
以下是该代码的Python单行等效代码:
l=[]
for i in range(3,5) :
    if i==3:
        for j in range(0,2):
            if i%2:
                l.append(i*j)
            else:
                l.append(i+j)
    else:
        l.append(i)
print(l)

我尝试使用单行嵌套循环,但只有一个条件,像这样:

print([i*j if i%2 else i+j for i in range(3,5) for j in range(0,2)])

注意:我想学习如何在两个循环中使用单行等效嵌套循环和if else条件。如果不可能,请说明原因! 提前感谢 :)

5
大多数代码不应该达到这种复杂程度。但如果确实需要这样做,最好像您所做的那样编写代码,以保持其功能性和可读性。列表推导(这是它们的实际名称)更适合于较简单的循环和条件语句。 - N Chauhan
1
我们中的一些人来自那些这种做法很自然的语言。我实际上喜欢这个问题。Python确实使嵌套比它本应该的更加困难。 - WestCoastProjects
我同意,这很有趣,但我一定要打开我的解释器开始尝试,以达到语法完美的效果。 - N Chauhan
三层嵌套是相当常规的,但 Python 并不把这视为优先事项(使用 for 推导可能只会使代码更加混乱)。 - matanster
5个回答

3
你可以将内部部分转换为列表序列:
[[i * j if i % 2 else i + j for j in range(2)] if i == 3 else [i] for i in range(3,5)]

在一般情况下,展开一个二维可迭代对象很容易:

[e for row in iterable for e in row]

结合两者:

[e for row in [[i * j if i % 2 else i + j for j in range(2)] if i == 3 else [i] for i in range(3,5)] for e in row]

您可以通过使用生成器来避免存储中间列表:
[e for row in ((i * j if i % 2 else i + j for j in range(2)) if i == 3 else [i] for i in range(3,5)) for e in row]

这很棒,但它是否规避了其他答案中人们提出的问题?(即:性能比等效的for循环差,而且非常难以阅读)(我会说它很难阅读,但看起来不像其他答案那样令人困扰) - bittahProfessional
@bittahProfessional。我不确定性能如何,但公平起见,问题可以重新表述为“如何使这段代码更糟糕”。如果您有一个良好的工作for循环,不是简单地遵循理解模式,请不要改变它。 - Mad Physicist

2
我想到的是为每个生成一个序列,然后使用包含式将它们展平成一个列表:
[x for y in ((i*j if i%2 else i+j for j in range(0, 2)) if i == 3 else (i,) for i in range(3, 5)) for x in y]
# [0, 3, 4]

很明显,这种方式比嵌套的版本更加复杂和难以阅读,并且性能可能更差。

2
这是一个可怕的丑陋物,它可以这样做:
[x for i in range(3,5) for x in ([i*j if i%2 else i+j for j in range(2)] if i==3 else (i,))]

我正在努力使这篇文章易读:

In [12]: result = [
    ...:     x
    ...:     for i in range(3, 5)
    ...:     for x in ((i*j if i%2 else i+j for j in range(2))
    ...:               if i ==3 else (i,))
    ...: ]

In [13]: print(result)
[0, 3, 4]

正如评论中指出的那样,你的循环不应该变得这么复杂。你应该在这里使用函数来使你的代码更易读。它也会使列表推导的使用更加灵活。你的目标不应该是“保持一行”,而应该是“保持我的代码可读、可维护、简单和诚实”。这个列表推导没有任何一个优点。
请注意,我并不希望这样做能够表现得更好。它需要创建额外的匿名容器或生成器/列表推导(需要在引擎盖下创建和调用函数对象)。
你编写代码的目标不应该是“如何学习编写越来越复杂的列表推导”,而应该是“如何编写和组织我的代码,以便我可以利用直观和可读的列表推导”。

这难道不会增加循环次数,从而增加代码的时间复杂度吗? 是的,我明白可读性应该是首要考虑因素,但我也想学习如何在两个循环中使用带有if-else块的列表推导式。 我的意思是出于好奇,想知道if语句将放置在哪里。 - Trifrost master
@Trifrostmaster "这样做是否增加了循环次数,从而增加了代码的时间复杂度?" 不是的,增加循环并不意味着增加复杂度。这只是一个经验法则,而不是逻辑推论。时间复杂度是等价的,(尽管我认为实际运行时间会更倾向于for循环版本而不是滥用列表推导式)。 - juanpa.arrivillaga
@Trifrostmaster 嗯,从学术角度来看这是没问题的,但在生产代码中你绝不能使用。这就是我想要明确的,因为我有责任感,知道这些问题经常被那些试图编写生产代码的人所查看。这种做法在一个合适的代码审查中会让你受到强烈谴责。 - juanpa.arrivillaga

1

我建议您尝试以下方式:

[(i*j if i % 2 and i==3
  else i+j if i==3
  else i)
 for j in range(0, 2)
 for i in range(3, 5)]

嵌套列表推导式的语法可能会变得非常混乱。这是输出结果:
[0, 4, 3, 4]

我猜你可以将这个变成一个集合来去重:
result = list(set(result))
# where result is the list above 

[0, 3, 4]

将其转换为集合,然后再转回列表可以解决问题,但这种方法并不可靠。 - N Chauhan

0
这是我想出来的代码: print(list(set([i*j if i % 2 else i+j if i==3 else i for i in range(3, 5) for j in range(0, 2)])))


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