有没有更好的方法在Python中编写连续的“或”语句?

时间:2012-06-25 12:55:38

标签: python conditional

我自己找不到任何“好”答案的简单问题:

假设我有以下条件:

if 'foo' in mystring or 'bar' in mystring or 'hello' in mystring:
    # Do something
    pass

根据具体情况,or陈述的数量可能会更长。

在不牺牲性能的情况下,是否有一种“更好”(更多Pythonic)的写作方式?

如果考虑使用any()但它需要一个类似布尔元素的列表,那么我必须首先构建该列表(在此过程中放弃短路评估),所以我猜它的效率较低

非常感谢。

4 个答案:

答案 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'更有可能的话,这只是一个问题比'酒吧'。