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

Python 从理解中消除理解中的冗余函数调用,python,list-comprehension,Python,List Comprehension,假设我们需要一个程序,它获取字符串列表并将其拆分,然后将元组中的前两个单词附加到列表并返回该列表;换句话说,是一个程序,它给出每个字符串的前两个单词 input: ["hello world how are you", "foo bar baz"] output: [("hello", "world"), ("foo", "bar")] 可以这样写(我们假设输入有效): 但是一份清单会更好 def firstTwoWords(strings): return [(s.split()[0

假设我们需要一个程序,它获取字符串列表并将其拆分,然后将元组中的前两个单词附加到列表并返回该列表;换句话说,是一个程序,它给出每个字符串的前两个单词

input: ["hello world how are you", "foo bar baz"]
output: [("hello", "world"), ("foo", "bar")]
可以这样写(我们假设输入有效):

但是一份清单会更好

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
>[(splt[0],splt[1])用于字符串中的s,s.split()作为splt]
文件“”,第1行
[(splt[0],splt[1])用于字符串中的s,s.split()作为splt]
^
SyntaxError:无效语法
像这样吗

def firstTwoWords(strings):
    return [s.split()[:2] for s in strings]
它使用列表拼接。当然,它会返回一个列表,但如果您想要一个元组,可以使用:

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

那么在这个特殊的情况下,

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)]

如果性能确实很关键,只需使用一个函数。

很遗憾,用英语自然写出脑海中的想法,并希望它的有效语法很少起作用

你要做的事情的一般形式是将一些表达式绑定到理解中的一个名称上。对此没有直接的支持,但由于理解中的
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')]

我认为使用genexp更好,但下面介绍如何使用
lambda
。在某些情况下,这可能更适合

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

minitech的答案是正确的

但请注意,您不必在一行中完成所有工作,您也不会真正获得任何东西

这:

做与此完全相同的事情:

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
ing/
yield from
ing最后一个genexpr/listcomp.

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')]

为了回答这个问题,我只举了一个简单的例子。我知道您可以在本例中执行切片,但当然不能对每种情况都执行该操作。@2rs2ts您还提到了哪些其他情况?我不确定我是否明白你的意思。我不知道,任何旧的东西。假设您正在处理由函数
foo()
返回的命名元组,这些元组具有
bar
baz
,并且可以具有许多其他部分,这取决于调用
foo()
——但您只需要
bar
baz
,并且不能依赖它们在元组中的位置。(注意:如果我说的命名元组不正确,我只想让SO知道,我对命名元组一无所知,只知道我可以对它们使用点语法。)@2rs2ts:换句话说,你知道命名元组的80%:)真的,这就是全部,除了定义它们的两种不同方式以及它们在封面下的工作方式之外。如果要将
一起使用,对象应该获得方法
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。此处无法使用列表。
输入是内置的,可能不是将其用作variable@gnibbler它不是一个变量-我使用代码块只是为了给它设置格式。我可能不应该使用
=
运算符。+1生成器表达式似乎就是问题所要问的。我很确定这就是我要找的-太糟糕了,我再过5分钟也不能接受了。我想这给了我时间来测试它。我打赌嵌套这些发电机会很有趣的!:)+1在这里最好使用
.split(无,2)
而不是
.split()
(因为我们只关心前两个词),只是作为旁白。@arshajii:是的,这可能会起作用-但这取决于需要做什么。(
.split(None,2)
最多可拆分为两个单词,而不是前两个单词。)我花了几次通读才明白“将名称依次绑定到序列中的每个元素”的含义,但一旦我明白了这一点,我就明白了——非常感谢!有时我的代码需要一个集合,有时一个iterable更好——这要看情况而定!不过,很高兴看到一些例子。(当然,我知道我不必在一行中做任何事情,我只是想看看如何完成。)@2rs2ts:记住,通过调用
list
,你总是可以将迭代器变成
list
,几乎是免费的,但另一种方法是绕过成本。(与编写显式函数或循环不同,“一行中的所有内容”的要点是,在这里您根本不需要更改流;您只需将表达式本身提取到自己的行中即可。)
>>> 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')]