22

我试图找出是否有一种方法可以将列表理解的每次迭代的值拆分一次,但在输出中使用两次。

作为我要解决的问题的一个示例,我有以下字符串:

a = "1;2;4\n3;4;5"

我想执行此操作:

>>> [(x.split(";")[1],x.split(";")[2]) for x in a.split("\n") if x.split(",")[1] != 5]
[('2', '4'), ('4', '5')]

无需运行拆分三遍。所以是这样的(这显然是无效的语法,但希望足以传达信息):

[(x[1],x[2]) for x.split(";") in a.split("\n") if x[1] != 5]

在这个问题中,我不是在寻找花哨的方法来获取字符串的第二列和第三列。这只是提供具体示例的一种方式。对于示例,我当然可以使用:

[x.split(";")[1:3] for x in a.split("\n")]

我想到的可能的解决方案:

  1. 不使用列表理解
  2. 保持原样
  3. 使用csv.DictReader, name my columns 之类的东西StringIO给它输入。

这主要是一种可以使用的很好的模式,而不是特定的情况,所以很难回答“你为什么要这样做”或“这是为了什么”之类的问题

更新:在阅读了下面的解决方案后,我进行了一些速度测试。我在非常基本的测试中发现,提供的解决方案比上面的简单解决方案快 35%。

4

2 回答 2

33

您可以使用包含在生成器表达式周围的列表推导:

[(x[1],x[2]) for x in (x.split(";") for x in a.split("\n")) if x[1] != 5]
于 2012-04-25T03:31:47.163 回答
7

开始Python 3.8,并引入赋值表达式(PEP 572):=运算符),可以在列表推导中使用局部变量以避免调用两次相同的表达式:

在我们的例子中,我们可以将 的求值命名line.split(';')为变量parts,同时使用表达式的结果来过滤列表 ifparts[1]不等于5; 并因此重新使用parts以产生映射值:

# text = '1;2;4\n3;4;5'
[(parts[1], parts[2]) for line in text.split('\n') if (parts := line.split(';'))[1] != 5]
# [('2', '4'), ('4', '5')]
于 2019-04-27T15:41:27.757 回答