这个 Python 列表推导式能简化吗?

3
input = "foo ,,bar ,baz,"
tags = [x.strip() for x in input.split(',') if len(x.strip()) > 0] 

期望的输出显然是一个没有空字符串的列表。
问题是在微观优化的精神中; 是否有一种方法可以不要两次对候选项x进行strip()操作,即一次用于测试,一次用于添加?
换句话说,您能否在表达式中生成一个值,该值可以附加到列表中而不必两次执行该操作?

我猜不会。如果.strip()缓存其结果(我认为不会),则可能会为您创建它,否则您将不得不以某种方式指示需要一个中间变量。 - Michael Clerx
3个回答

9
创建新字符串的成本总是比扫描它更高。在遇到第一个非空格字符后,x.isspace()将返回。
tags = [x.strip() for x in input.split(',') if x and not x.isspace()]

是的。而且它比我的任何一个都快。(1.6秒对比1.95秒。) - Chris Morgan

3
text = 'foo ,,bar ,baz,'

(我使用text代替input,因为input是内置函数名称。避免与内置函数重名。)

首先,len(x.strip()) > 0可以简单(且更有效率地)写作x.strip()

tags = [x.strip() for x in text.split(',') if x.strip()]

如果你真的想要,你可以只做一次带状体,但我不确定这样是否更快:

tags = [x for x in (x.strip() for x in text.split(',')) if x]

如果你真的想要,甚至可以用函数式方法实现...
tags = filter(bool, map(lambda x: x.strip(), text.split(',')))

性能数据:

>>> from timeit import timeit
>>> timeit(lambda: [x.strip() for x in text.split(',') if x.strip()])
1.9443869590759277
>>> timeit(lambda: [x for x in (x.strip() for x in text.split(',')) if x])
2.1135239601135254
>>> timeit(lambda: filter(bool, map(lambda x: x.strip(), text.split(','))))
2.52907395362854

正如您所看到的,第一个是最快的。


好的,谢谢你的提示,很有用。"首先..."这句话是否意味着你可以解释一下重复工作的部分? - markdsievers
@markdsievers:是的,这样更整洁;但如果这是一个特别热门的代码路径,所以你关心微观优化,@gnibbler的if x and not x.isspace()if x.strip()更快。 - Chris Morgan
1
是的,这绝对不是一个能够引起轰动的应用程序,所以我只是在寻找性能、重复和可读性之间的正确平衡。再次感谢您的努力,非常有见地。 - markdsievers

1

这也可以运行...

 text = "foo ,,bar ,baz,"
 text.replace(',',' ').split()

1
不,这并非在所有情况下都适用,例如 text = "foo,,bar,,baz"。将 ',' 替换为空格,即 text.replace(',', ' ').split() 更好。 - mhawke
2
这会在 "foo bar, baz" 处出现问题,将其分成 3 个项目而不是 2 个。 - Karl Knechtel

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