我自己找不到任何“好”答案的简单问题:
假设我有以下条件:
if 'foo' in mystring or 'bar' in mystring or 'hello' in mystring:
# Do something
pass
根据具体情况,or
陈述的数量可能会更长。
在不牺牲性能的情况下,是否有一种“更好”(更多Pythonic)的写作方式?
如果考虑使用any()
但它需要一个类似布尔元素的列表,那么我必须首先构建该列表(在此过程中放弃短路评估),所以我猜它的效率较低
非常感谢。
答案 0 :(得分:30)
一种方式可能是
if any(s in mystring for s in ('foo', 'bar', 'hello')):
pass
你迭代的东西是一个元组,它是在编译函数的基础上构建的,所以它不应该低于原始版本。
如果你担心元组会变得太长,你可以做到
def mystringlist():
yield 'foo'
yield 'bar'
yield 'hello'
if any(s in mystring for s in mystringlist()):
pass
答案 1 :(得分:7)
这听起来像是一个正则表达式的工作。
import re
if re.search("(foo|bar|hello)", mystring):
# Do something
pass
它也应该更快。特别是如果你提前编译正则表达式。
如果您自动生成正则表达式,可以使用re.escape()
确保没有特殊字符破坏正则表达式。例如,如果words
是您要搜索的字符串列表,则可以生成如下图案:
pattern = "(%s)" % ("|".join(re.escape(word) for word in words), )
您还应注意,如果您有m
个字词且字符串中包含n
个字符,则原始代码的复杂度为O(n*m)
,而正则表达式的复杂度为O(n)
。即使Python正则表达式不是理论上的comp-sci正则表达式,并且总是 O(n)
复杂性,在这个简单的例子中它们也是。
答案 2 :(得分:2)
由于您正在对mystring
进行逐字处理,因此mystring肯定可以用作一个集合。然后,只需获取包含mystring
中单词的集合与目标词组之间的交集:
In [370]: mystring=set(['foobar','barfoo','foo'])
In [371]: mystring.intersection(set(['foo', 'bar', 'hello']))
Out[371]: set(['foo'])
您的逻辑“或”是两组交集的成员。
使用套装也更快。以下是与生成器和正则表达式的相对时序:
f1: generator to test against large string
f2: re to test against large string
f3: set intersection of two sets of words
rate/sec f2 f1 f3
f2 101,333 -- -95.0% -95.5%
f1 2,026,329 1899.7% -- -10.1%
f3 2,253,539 2123.9% 11.2% --
因此,生成器和in
操作比正则表达式快19倍,并且集合交集比正则表达式快21倍,比生成器快11%。
以下是生成时间的代码:
import re
with open('/usr/share/dict/words','r') as fin:
set_words={word.strip() for word in fin}
s_words=' '.join(set_words)
target=set(['bar','foo','hello'])
target_re = re.compile("(%s)" % ("|".join(re.escape(word) for word in target), ))
gen_target=(word for word in ('bar','foo','hello'))
def f1():
""" generator to test against large string """
if any(s in s_words for s in gen_target):
return True
def f2():
""" re to test against large string """
if re.search(target_re, s_words):
return True
def f3():
""" set intersection of two sets of words """
if target.intersection(set_words):
return True
funcs=[f1,f2,f3]
legend(funcs)
cmpthese(funcs)
答案 3 :(得分:2)
如果您有要检查的已知项目列表,您也可以将其写为
if mystring in ['foo', 'bar', 'hello']:
你可能无法获得确保比较顺序的好处(我不认为Python需要从左到右检查列表元素)但如果你知道'foo'更有可能的话,这只是一个问题比'酒吧'。