带有重复函数调用的列表推导式

16

我想要转换像以下这样的字符串:

'   1   ,   2  ,    ,   ,   3   '

转换为一个非空元素列表:

['1', '2', '3']

我的解决方案是这个列表推导式:

print [el.strip() for el in mystring.split(",") if el.strip()]

不知道是否有一种好的、符合Python风格的方法,可以在不调用el.strip()两次的情况下编写这个推导式?


这个字符串从哪里来?它是如何创建的? - Chris_Rands
1
重复的问题[1](https://dev59.com/eV8d5IYBdhLWcg3wkS7Y)[2](https://stackoverflow.com/q/40539357/2301450)[3](https://dev59.com/yJ3ha4cB1Zd3GeqPSEzj)[4](https://dev59.com/X2Uo5IYBdhLWcg3wtRfh)。只需将此问题标题输入谷歌搜索。 - vaultah
7个回答

22
你可以在列表推导式中使用生成器:

您可以在列表推导式中使用生成器:

  [<b>x for x in (el.strip()</b> for el in mylist.split(",")<b>)</b> if <b>x</b>]
#             \__________________ ___________________/
#                                v
#                        internal generator
生成器会提供剥离后的元素,我们迭代生成器,并仅检查真实性。这样我们就可以节省掉el.strip()的调用。 您也可以使用map(..)来实现这一点(使其更加函数化):

生成器将提供剥离的元素, 我们迭代生成器, 并只检查真实性. 这样做可以节省对 el.strip() 的调用。

您还可以使用map(..) 来实现这一点(使其更具函数式风格):

  [x for x in <b>map(str.strip,</b> mylist.split(",")<b>)</b> if x]
#             \______________ ________________/
#                            v
#                           map

但基本上这是一样的(尽管生成器的逻辑在我看来更好地封装了)。


8
作为获取非空元素列表的简单替代方法(除了之前的好答案之外),可以使用以下代码:
import re

s = '   1   ,   2  ,    ,   ,   3   '
print(re.findall(r'[^\s,]+', s))

输出结果:
['1', '2', '3']

5

来试试使用正则表达式从字符串中提取所有数字吧。

import re

a = '   1   ,   2  ,    ,   ,   3   '
print(re.findall(r'\d+', a))

输出:

['1', '2', '3']

3
在这只有一行代码,它非常简洁。当然,如果你想要更加高级的方法,可以尝试使用函数式编程:
filter(lambda x: x, map(lambda x: x.strip(), mylist.split(',')))

但是这样做会以简洁为代价失去可见性。


我觉得 map 有点过头了。你可以直接把 OP 的理解放进去,不需要 if - Ma0
1
我想我会一路走函数式编程的路线,不会“破坏”它 :) - omu_negru
6
如果你坚持使用函数式方法,那么 filter(None,map(str.strip, mystring.split(","))) 会变得更加简洁。需要翻译的内容已完成。 - Chris_Rands
不知道 None 部分... 但是 str.strip 应该很明显。谢谢 :) - omu_negru
我喜欢这个解决方案。然而,我决定接受Willem的答案,想要坚持使用生成器... - peter.slizik
显示剩余2条评论

2

通过使用mapfilter,全面实现功能:

s = '   1   ,   2  ,    ,   ,   3   '
res = filter(None, map(str.strip, s.split(',')))

尽管与 @omu_negru 的答案类似,但这种方法避免使用lambda表达式,这些表达式被认为非常丑陋,而且会减慢速度。
对于过滤器的None参数进行过滤,相当于在可迭代对象上过滤真值,本质上是 x for x in iterable if x,而map将方法str.strip(其默认分割值为空格)映射到从s.split(',')获得的可迭代对象上。
在Python 2中,由于filter仍然返回列表,因此这种方法的速度应该很快地超越其他方法。
在Python 3中,需要使用:
res = [*filter(None, map(str.strip, s.split(',')))]

为了获取列表,请执行以下操作。

-1
如果你已经导入了"re",那么re.split()就会起作用:
import re
s='   1   ,   2  ,    ,   ,   3   '
print ([el for el in re.split(r"[, ]+",s) if el])
['1', '2', '3']

如果不希望只由空格分隔的字符串被分开,而没有逗号干扰,则可以使用以下代码:
import re
s=' ,,,,,     ,,,,  1   ,   2  ,    ,   ,   3,,,,,4   5, 6   '
print ([el for el in re.split(r"\s*,\s*",s.strip()) if el])
['1', '2', '3', '4   5', '6']

但这也会删除元素之间的空格。例如 ' a b, qux foo, 1 2 3'。将导致 'a','b','qux','foo','1','2','3' - Willem Van Onsem
@WillemVanOnsem,不是很清楚,但OP只想要数字,而不是字母数字混合字符串。请查看RomanPerekhrest的答案,他使用了正则表达式r'[^\s,]+'(获得6个赞成票且没有负面评论,但它隐含着你提到的同样的问题);请查看Miraj50的答案,他说“从字符串中提取所有数字”(获得3个赞成票且没有负面评论,但它明确地引用了你指出的同样的问题);我知道你在说什么,我特意在我的回答中包括了“4 5”。我想知道为什么我的答案这么差或错了,只能得到负票.... - Prem
我没有点踩。我同意相同的评论适用于RomanPerekhrest。然而,通常人们更喜欢通用的、强大和灵活的方法,而不是只能解决特定问题的方法:如果以后OP稍微改变了想法,那么这个解决方案可能就无法使用了。 - Willem Van Onsem
@WillemVanOnsem,感谢您的反馈。我对我的答案进行了微小的编辑。我已经为您的正确答案点赞。[[如果OP稍后改变了想法,那么无论他使用您的片段还是我的片段或其他内容,他都必须稍微更改代码]]。 - Prem

-1
列表推导式很棒,但使用多行代码并不违法!你甚至可以 - 天哪 - 使用 for 循环!
result = []
for el in mystring.split(",")
    x = el.strip()
    if x:
        result.append(x)

这是一个两行版本。实际上,它与Willem Van Onsem的被接受答案相同,但给子表达式命名(并将生成器更改为列表,但对于这么小的问题基本没有影响)。在我看来,尽管需要稍微多写一点代码,但这使得代码更易读。
all_terms = [el.strip() for el in mystring.split(",")]
non_empty_terms = [x for x in all_terms if x]

其他答案中有些确实更短,但我并不认为它们更简单易懂。实际上,我认为最好的答案就是你问题中的那个,因为这种情况下的重复相当少。


1
我很希望能够得到有关负投票的反馈。是因为讽刺(无疑是不必要的)还是选民真的那么反对将代码拆分成多个语句以提高清晰度? - Arthur Tacca

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接