Python 将带有赋值的循环转换为理解

Python 将带有赋值的循环转换为理解,python,Python,将循环转换为理解非常简单: mylist = [] for word in ['Hello', 'world']: mylist.append(word.split('l')[0]) 到 但是当循环涉及到给引用赋值时,我不确定如何继续 mylist = [] for word in ['Hello', 'world']: split_word = word.split('l') mylist.append(split_word[0]+split_word[1]) 最终

将循环转换为理解非常简单:

mylist = []
for word in ['Hello', 'world']:
    mylist.append(word.split('l')[0])

但是当循环涉及到给引用赋值时,我不确定如何继续

mylist = []
for word in ['Hello', 'world']:
    split_word = word.split('l')
    mylist.append(split_word[0]+split_word[1])
最终的理解是这样的:

mylist = [word.split('l')[0]+word.split('l')[1] for word in ['Hello', 'world']]
这将多次计算
word.split('l')
,而循环只计算一次并保存一个引用。我尝试了以下方法:

mylist = [split_word[0]+split_word[1] for word in ['Hello', 'world'] with word.split('l') as split_word]
这会失败,因为
with
不能以这种方式工作,并且:

mylist = [split_word[0]+split_word[1] for word in ['Hello', 'world'] for split_word = word.split('l')]
这也不行。我知道可以通过
*
***
解包,但我不确定这是否适合这里。是否有可能将这些循环转化为理解,希望以一种整洁的方式?

要有创造性

mylist = [''.join(word.split('l')[:2]) for word in ['Hello', 'world']]


你不能直接将这个循环转化为理解。理解是表达式,只能包含表达式,赋值是语句

然而,这并不意味着没有选择

首先,以调用
split
两次为代价,您只需执行以下操作:

mylist = [word.split('l')[0]+word.split('l')[1] for word in ['Hello', 'world']]
但是您不想调用
split
两次


最常用的方法是使用一系列生成器表达式(末尾有一个列表理解)来转换内容:

words = (word.split('l') for word in ['Hello', 'world'])
mylist = [w[0]+w[1] for w in words]
如果确实要将所有内容合并到一个表达式中,可以:

mylist = [w[0]+w[1] for w in 
          (word.split('l') for word in ['Hello', 'world'])]
但除非你真的需要它在表达式中,否则不这样做可能更容易理解


在这种情况下,更具体的方法是将
w[0]+w[1]
替换为不需要引用
w
两次的等价物:

mylist = [''.join(word.split('l')[:2]) for word in ['Hello', 'world']]
你也可以把这个推广。您可以将任何表达式转换为函数,这意味着您可以通过将其作为参数传递给该函数来避免对其任何部分求值。如果没有满足您要求的函数,请编写:

def join_up(split_word):
    return split_word[0]+split_word[1]
mylist = [join_up(word.split('l')) for word in ['Hello', 'world']]
如果您需要在不重复任何工作的情况下将所有内容都转换为一个表达式,那么它可能并不漂亮:

mylist = [(lambda split_word: split_word[0]+split_word[1])(word.split('l')) 
          for word in ['Hello', 'world']]

但最终,除非我已经有了一个可以满足我需要的函数,否则我会使用生成器表达式链解决方案


当然,也可以将其保持在显式循环中;
for
循环没有错,如果中间临时变量使代码更清晰,那么没有比赋值语句更好的方法了。

赋值就是一个语句。列表理解仅适用于表达式。是否有任何方法可以执行一次计算,然后在不重新计算的情况下多次引用其结果,就像
word
(在示例中)可以在理解的范围内多次引用一样?在python3中,您可以执行例如
[word.split('l')]中的(first,second,*rest)的_列表中的单词的[first+second]
即使在一般情况下有一种干净的方法可以做到这一点,你真正要做的就是水平编写代码,而不是垂直编写。不清楚这会给你带来什么。@DSM:另外,现有的代码为中间值提供了一个清晰的名称;虽然有一些方法可以将其嵌入到一个大表达式中,但没有一种方法是正确的我已经向您介绍了赋值语句的可读性优势。很好,我认为使用生成器表达式是解决这一常见问题的最具python风格的方法。@wim:是的,这就是为什么我把它放在第一位。我想最后构建一个参数传递解决方案,因为从概念上看,它应该是一样好的(比如说,Haskell),但这在实践中是非常可怕的,除非你已经有了一个可以满足你需要的越界函数。我过去常常链接理解(例如
[c为c在[b为b在a]]
),直到我发现我是从正常的方式向后做的(例如
[c为b在a为c在b]
)。很高兴知道它们不是同一件事,只是在某些向后的值上是向后的。:)我会记住这个解决方案,当单一理解不合适时。我以前从未使用过
itemgetter
,它似乎是理想的工具,用于需要比切片更精确的东西的情况@TigerhawkT3它也适用于dict键,而dict键则不适用。但大多数情况下,您只在需要函数时使用itemgetter(传递到map或takewhile,因此您往往忘记它也可以执行“不连续切片”。
def join_up(split_word):
    return split_word[0]+split_word[1]
mylist = [join_up(word.split('l')) for word in ['Hello', 'world']]
mylist = [(lambda split_word: split_word[0]+split_word[1])(word.split('l')) 
          for word in ['Hello', 'world']]