从理解中消除冗余函数调用的理解

时间:2013-07-10 01:17:43

标签: python list-comprehension

假设我们需要一个程序,它接受一个字符串列表并将它们拆分,然后将一个元组中的前两个单词追加到一个列表中并返回该列表;换句话说,一个程序,它给你每个字符串的前两个单词。

input: ["hello world how are you", "foo bar baz"]
output: [("hello", "world"), ("foo", "bar")]

可以像这样编写(我们假设有效输入):

def firstTwoWords(strings):
    result = []
    for s in strings:
        splt = s.split()
        result.append((splt[0], splt[1]))
    return result

但列表理解会更好。

def firstTwoWords(strings):
    return [(s.split()[0], s.split()[1]) for s in strings]

但这涉及到split()的两次调用。 有没有办法在理解中只执行一次拆分?我尝试了自然而然的语法无效:

>>> [(splt[0],splt[1]) for s in strings with s.split() as splt]
  File "<stdin>", line 1
    [(splt[0],splt[1]) for s in strings with s.split() as splt]
                                           ^
SyntaxError: invalid syntax

6 个答案:

答案 0 :(得分:6)

嗯,在这个特殊情况下:

def firstTwoWords(strings):
    return [s.split()[:2] for s in strings]

否则,您可以使用一个生成器表达式:

def firstTwoWords(strings):
    return [(s[0], s[1]) for s in (s.split() for s in strings)]

如果性能实际上很重要,那就使用一个函数。

答案 1 :(得分:4)

不幸的是,写出自然会从英语中浮现的想法并希望它的有效语法很少有用。

您尝试做的事情的一般形式是将一些表达式绑定到理解中的名称。没有直接支持,但由于理解中的for子句依次将名称绑定到序列中的每个元素,因此可以使用for而不是单个元素容器来实现相同的效果:

>>> strings = ["hello world how are you", "foo bar baz"]
>>> [(splt[0],splt[1]) for s in strings for splt in [s.split()]]
[('hello', 'world'), ('foo', 'bar')]

答案 2 :(得分:2)

我认为使用genexp更好,但是如何使用lambda进行操作。可能存在更合适的情况

>>> [(lambda splt:(splt[0], splt[1]))(s.split()) for s in input]
[('hello', 'world'), ('foo', 'bar')]

答案 3 :(得分:2)

minitech的答案是正确的方法。

但请注意,您不必在一行中完成所有操作,而且您实际上并没有获得任何收益。

此:

splits = (s.split() for s in strings)
return [(s[0], s[1]) for s in splits]

与此完全相同:

return [(s[0], s[1]) for s in (s.split() for s in strings)]

没有构建额外的中间值,对垃圾收集没有影响,只是免费提供更多可读性。

此外,您的真实代码很可能实际上不需要最终列表,只是可迭代的东西,在这种情况下,您最好用这个:

splits = (s.split() for s in strings)
return ((s[0], s[1]) for s in splits)

或者,在Python 3.3 +中:

splits = (s.split() for s in strings)
yield from ((s[0], s[1]) for s in splits)

事实上,很多程序都可以用这种方式编写 - 一系列生成器表达式,后跟return / {{}}最后一个genexpr / listcomp。

答案 4 :(得分:1)

喜欢这个吗?

def firstTwoWords(strings):
    return [s.split()[:2] for s in strings]

它使用列表拼接。它将返回一个列表,但如果你想要一个元组,你可以使用:

def firstTwoWords(strings):
    return [tuple(s.split()[:2]) for s in strings]

答案 5 :(得分:0)

itemgetter可以在这里使用。它比s.split()[:2]更普遍。它允许您从s

中提取任意项目
>>> from operator import itemgetter
>>> strings = ["hello world how are you", "foo bar baz"]
>>> [itemgetter(0, 1)(s.split()) for s in strings]
[('hello', 'world'), ('foo', 'bar')]

更一般地说:

>>> [itemgetter(1, 2, 0)(s.split()) for s in strings]
[('world', 'how', 'hello'), ('bar', 'baz', 'foo')]