在正式的 for 循环中使用 else 子句,但在列表理解中,这可能吗?

Use an else clause like the ones in formal for loops but in a list comprehension, is it possible?

这是我的初始列表列表的示例:

trip_chains = [['London', 'Paris', 'Madrid'], 
               ['Delhi', 'London', 'New York'],
            ...['Jerusalem', 'Cairo', 'Paris']]

我想从 trip_chain 中提取另一个列表,如下所示:

chains_steps = [[['London', 'Paris'], ['Paris', 'Madrid'], ['Madrid', 'London']], 
                [['Delhi', 'London'], ['London', 'New York'], ['New York', 'Delhi']],
             ...[['Jerusalem', 'Cairo'], ['Cairo', 'Paris'], ['Paris', 'Jerusalem']]]

所以我使用了一个包含两个 for 循环的列表推导式:

chains_steps = [[chain_[stepscount_], chain_[stepscount_+1]] for chain_ in trip_chains for stepscount_ in range(len(chain_)-1)]

但是因为我失去了返回出发城市的步骤,结果将如下所示:

chains_steps = [[['London', 'Paris'], ['Paris', 'Madrid']], 
                [['Delhi', 'London'], ['London', 'New York']],
             ...[['Jerusalem', 'Cairo'], ['Cairo', 'Paris']]]

因为我们可以把这个列表理解看作是一个双重嵌套的for循环, 我们知道 for 循环带有一个可选的 else 子句:

chain_steps = []
for chain_ in trip_chains:
    for stepscount_ in range(len(chain_)-1):
        chain_steps.append([chain_[stepscount_], chain_[stepscount_+1])
    else:
        chain_steps.append([chain_[-1], chain_[0]])

我想知道我是否可以这样在 列表理解 中使用这个 else 子句:

    chains_steps = [[chain_[stepscount_], chain_[stepscount_+1]] for chain_ in trip_chains for stepscount_ in range(len(chain_)-1) else [chain_[-1], chain_[0]]]

我知道对于这个特定问题可能有很多替代解决方案,但我是出于好奇和知识才问的。

P.S。每次内循环结束后,人们可能想要 运行 一个函数。所以这个问题不是我的例子特有的。

谢谢。

不,您不能在列表理解上有 else 个分支。

您可以循环 chain + [chain[0]] 或使用 % 运算符将步数“折叠”回开头。或者只是手动添加最后一对,正如@mkrieger 在评论中建议的那样。

另一个新答案

基于S.Khajeh'评论,我添加了另一个单行解决方案,即更通用覆盖有笔画的情况具有不均匀的无限制行程长度

修改为比双重比较更有效 :)

chains_steps = [[[row[i], (row + [row[0]])[i + 1]] for i in range(len(row))] for row in trip_chains]

之前

chains_steps = [[[(row + row)[i], (row + row)[i + 1]] for i in range(len(row))] for row in trip_chains]

旧答案

您也可以通过直接编码如下行来实现您的目标:

chains_steps = [ [ [row[0],row[1]] , [row[1],row[2]] , [row[2],row[0]] ]  for row in trip_chains]

输出是

chains_steps
Out[7]: 
[[['London', 'Paris'], ['Paris', 'Madrid'], ['Madrid', 'London']],
 [['Delhi', 'London'], ['London', 'New York'], ['New York', 'Delhi']],
 [['Jerusalem', 'Cairo'], ['Cairo', 'Paris'], ['Paris', 'Jerusalem']]]

如你所愿。

for...else

我建议你学习for...else语义。 @mkrieger1 的评论可能有点简洁。 doc states:

a loop’s else clause runs when no break occurs.

例如:

>>> for i in range(1):
...     print("first and only iteration")
...     break
... else: # not executed since there is a break
...     print("no break was met")
first and only iteration

但是:

>>> for i in range(1):
...     print("first and only iteration")
... else: # executed since the loop exits normally
...     print("no break was met")
first and only iteration
no break was met

如果循环体中没有发生 break,则 else 子句总是 运行, 因此以下代码段是等效的:

for ...:
    <loop body>
else:
    <else statement>

并且:

for ...:
    <loop body>
<else statement>

列表的列表还是列表的列表?

你写:

chain_steps = []
for chain_ in trip_chains:
    for stepscount_ in range(len(chain_)-1):
        chain_steps.append([chain_[stepscount_], chain_[stepscount_+1]])
    else:
        chain_steps.append([chain_[-1], chain_[0]])

正如@Ture Pålsson 所写,这只会产生一个列表列表。正确的版本是:

trip_chains = [['London', 'Paris', 'Madrid'],
               ['Delhi', 'London', 'New York'],
               ['Jerusalem', 'Cairo', 'Paris']]

chain_steps = []
for chain_ in trip_chains:
    chain_step = [] # create a new list in the outer loop
    # and fill it in the inner loop
    for stepscount_ in range(len(chain_)-1):
        chain_step.append([chain_[stepscount_], chain_[stepscount_+1]])
    chain_step.append([chain_[-1], chain_[0]])
    chain_steps.append(chain_step)

from pprint import pprint
pprint(chain_steps)

输出:

[[['London', 'Paris'], ['Paris', 'Madrid'], ['Madrid', 'London']],
 [['Delhi', 'London'], ['London', 'New York'], ['New York', 'Delhi']],
 [['Jerusalem', 'Cairo'], ['Cairo', 'Paris'], ['Paris', 'Jerusalem']]]

不需要“初等数学”。

我们可以在列表理解中添加 else 子句吗?

主要问题是:我们可以在列表理解中使用 break 吗?答案是否定的(Python 3):

>>> [i if i<5 else break for i in range(10)]
Traceback (most recent call last):
    [i if i<5 else break for i in range(10)]
                       ^
SyntaxError: invalid syntax

(有关详细信息,请参阅 Using break in a list comprehension)。

因此,else 子句在列表理解中没有意义。结果:我们不能在列表理解中有 else 子句。

我们可以在列表理解中添加额外的元素吗?

我将重点介绍内循环:

chain_ = ['London', 'Paris', 'Madrid']

chain_step = []
for stepscount_ in range(len(chain_)-1):
    chain_step.append([chain_[stepscount_], chain_[stepscount_+1]])
chain_step.append([chain_[-1], chain_[0]]) # extra element

pprint(chain_step)

输出:

[['London', 'Paris'], ['Paris', 'Madrid'], ['Madrid', 'London']]

显然,我们可以用最后一个元素添加一个迭代:

chain_step = []
for stepscount_ in list(range(len(chain_)-1))+[-1]:
    chain_step.append([chain_[stepscount_], chain_[stepscount_+1]])

然后:

>>> chain_ = ['London', 'Paris', 'Madrid']
>>> [[chain_[s], chain_[s+1]] for s in list(range(len(chain_)-1))+[-1]]
[['London', 'Paris'], ['Paris', 'Madrid'], ['Madrid', 'London']]

当你可以这样写时,这有点矫枉过正:

>>> chain_ = ['London', 'Paris', 'Madrid']
>>> [[chain_[s], chain_[s+1]] for s in range(len(chain_)-1)] + [[chain_[-1], chain_[0]]]
[['London', 'Paris'], ['Paris', 'Madrid'], ['Madrid', 'London']]

(您可以轻松地将其包装在外部列表理解中:

chain_steps = [
    [[chain_[s], chain_[s+1]] for s in range(len(chain_)-1)] + [[chain_[-1], chain_[0]]]
    for chain_ in trip_chains
]

)

奖金:有没有更简洁的方式来写这个?

YES(内循环):

chain_step = []
for step in zip(chain_, chain_[1:]+[chain_[0]]):
    chain_step.append(step)

pprint(chain_step)

输出:

[('London', 'Paris'), ('Paris', 'Madrid'), ('Madrid', 'London')]

zip 函数将对元素进行配对:

chain[0], chain[1], ... chain_[n-2], chain_[n-1]  <-  chain_
chain[1], chain[2], ... chain_[n-1], chain_[0]    <-  chain_[1:]+[chain_[0]]

如您所见,您有一个元组列表,而不是列表列表。这样更好,因为一个步骤是一对,而不是一个列表(元组大小不会改变,列表大小可能会改变)。

因此最终的双列表理解:

>>> trip_chains = [['London', 'Paris', 'Madrid'],
...                ['Delhi', 'London', 'New York'],
...                ['Jerusalem', 'Cairo', 'Paris']]
>>> [[step for step in zip(chain_, chain_[1:]+[chain_[0]])] for chain_ in trip_chains]
[[('London', 'Paris'), ('Paris', 'Madrid'), ('Madrid', 'London')], [('Delhi', 'London'), ('London', 'New York'), ('New York', 'Delhi')], [('Jerusalem', 'Cairo'), ('Cairo', 'Paris'), ('Paris', 'Jerusalem')]]

或者,因为内部列表推导式只取 zip:

的元素
>>> [list(zip(chain_, chain_[1:]+[chain_[0]])) for chain_ in trip_chains]
[[('London', 'Paris'), ('Paris', 'Madrid'), ('Madrid', 'London')], [('Delhi', 'London'), ('London', 'New York'), ('New York', 'Delhi')], [('Jerusalem', 'Cairo'), ('Cairo', 'Paris'), ('Paris', 'Jerusalem')]]

注意:代码变得难以维护...