有没有更好的方法来写“连续”呢;或;Python中的语句?
我自己找不到任何“好”答案的简单问题: 假设我有以下条件:有没有更好的方法来写“连续”呢;或;Python中的语句?,python,conditional,Python,Conditional,我自己找不到任何“好”答案的简单问题: 假设我有以下条件: if 'foo' in mystring or 'bar' in mystring or 'hello' in mystring: # Do something pass 根据情况,或语句的数量可能会更长 在不牺牲性能的情况下,有没有一种“更好的”(更像蟒蛇)的写作方式 如果考虑使用any(),但它需要一个类似布尔元素的列表,因此我必须首先构建该列表(在这个过程中放弃短路计算),因此我认为它效率较低 非常感谢。一种方法
if 'foo' in mystring or 'bar' in mystring or 'hello' in mystring:
# Do something
pass
根据情况,或
语句的数量可能会更长
在不牺牲性能的情况下,有没有一种“更好的”(更像蟒蛇)的写作方式
如果考虑使用any()
,但它需要一个类似布尔元素的列表,因此我必须首先构建该列表(在这个过程中放弃短路计算),因此我认为它效率较低
非常感谢。一种方法是
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
这听起来像是正则表达式的工作
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正则表达式,也不总是复杂的,但在这个简单的例子中,它们是复杂的。因为您是针对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% --
因此,生成器和操作中的比正则表达式快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)
如果您有一个已知的要检查的项目列表,您也可以将其写为
if mystring in ['foo', 'bar', 'hello']:
您可能无法获得确保比较顺序的好处(我认为Python不需要从左到右检查列表元素),但如果您知道“foo”比“bar”更可能出现,那么这只是一个问题。谢谢。但这项技术不是阻止了短路优化吗?它是一个生成器,而不是列表。不是(mystring中的s表示“foo”、“bar”、“hello”中的s)
是一个生成器表达式,这意味着它不是作为一个整体立即计算的,而是按需计算的any()
在看到第一个真值时停止迭代,因此不会检查其余值。仔细阅读生成器表达式。@gnibbler:我没有说过任何都不会短路。我担心名单的结构不会。但是因为它实际上是一个发电机,它改变了一切。哦,如果你做了any([s in mystring for s in…])
,你就不会短路。[]
列表理解总是创建一个完整的列表any()
不会对其进行迭代,但在调用any
之前仍会对其进行计算。但是,如果要查找的任何“单词”包含特殊的正则表达式,则必须小心characters@gnibbler:是的。相反,通过使用模式匹配,您可能可以编写更少的代码。如果您正在执行自动生成正则表达式之类的操作,则可以使用re.escape()
。确实可以,您应该将其添加到回答中谢谢您的建议。我的真实案例场景与这种技术不太兼容(但你显然猜不到)。投票赞成公平,因为答案是正确的,可以帮助别人。确实更快!可能是因为正则表达式只需要遍历字符串一次。这与公认的答案有何不同?唯一的区别是您使用的是集合
而不是元组
,否则就完全一样了。@cha0site:公认的答案还提出了一个用于大列表的函数。我认为一套是更好的方式,使相同的。这也提出了两套——不使用any
这不是一回事。当mystring='helloworld'
时,问题中的条件也会成立。这一个不会。很好的一点,谢谢-正如你所说,不完全一样的事情,也许不适合具体的问题。