有效地检查该string是否由Python中的一个字符组成

什么是有效的方法来检查Python中的string是否只包含一个字符,如'A' ? 类似all_equal(s, 'A')行为就像这样:

 all_equal("AAAAA", "A") = True all_equal("AAAAAAAAAAA", "A") = True all_equal("AAAAAfAAAAA", "A") = False 

两种表面上效率低下的方法是:首先将string转换为列表并检查每个元素,或者使用正则expression式。 是否有更有效的方法,或者这些是Python中最好的方法? 谢谢。

这是迄今为止最快的,甚至比count()还要快几倍,只需要用那个优秀的mgilson的计时套件来计时 :

 s == len(s) * s[0] 

这里所有的检查都是在Python C代码里完成的,只是:

  • 分配len(s)个字符;
  • 用第一个字符填充空间;
  • 比较两个string。

弦越长,时间奖励就越大。 然而,正如mgilson写的,它创build了一个string的副本,所以如果你的string长度是数百万个符号,那么可能会成为一个问题。

正如我们从计时结果中可以看到的,通常解决任务的最快方法是不为每个符号执行任何Python代码。 但是, set()解决scheme也是在Python库的C代码中完成所有工作,但是它仍然很慢,可能是因为通过Python对象接口操作string。

UPD:关于空string的情况。 如何处理它强烈依赖于任务。 如果任务是“检查一个string中的所有符号是否相同”, s == len(s) * s[0]是一个有效的答案(没有符号表示错误,exception是好的)。 如果任务是“检查是否只有一个唯一的符号”,空string应该给我们False,答案是s and s == len(s) * s[0] ,或者bool(s) and s == len(s) * s[0]如果你喜欢接收布尔值。 最后,如果我们将任务理解为“检查是否没有不同的符号”,则空string的结果为True,并且答案not s or s == len(s) * s[0]

 >>> s = 'AAAAAAAAAAAAAAAAAAA' >>> s.count(s[0]) == len(s) True 

这不会短路。 一个短路的版本是:

 >>> all(x == s[0] for x in s) True 

不过,我有一个感觉,由于优化的C实现,非短路版本可能会执行更好的一些string(取决于大小等)


这里有一个简单的timeit脚本来testing一些其他的发布选项:

 import timeit import re def test_regex(s,regex=re.compile(r'^(.)\1*$')): return bool(regex.match(s)) def test_all(s): return all(x == s[0] for x in s) def test_count(s): return s.count(s[0]) == len(s) def test_set(s): return len(set(s)) == 1 def test_replace(s): return not s.replace(s[0],'') def test_translate(s): return not s.translate(None,s[0]) def test_strmul(s): return s == s[0]*len(s) tests = ('test_all','test_count','test_set','test_replace','test_translate','test_strmul','test_regex') print "WITH ALL EQUAL" for test in tests: print test, timeit.timeit('%s(s)'%test,'from __main__ import %s; s="AAAAAAAAAAAAAAAAA"'%test) if globals()[test]("AAAAAAAAAAAAAAAAA") != True: print globals()[test]("AAAAAAAAAAAAAAAAA") raise AssertionError print print "WITH FIRST NON-EQUAL" for test in tests: print test, timeit.timeit('%s(s)'%test,'from __main__ import %s; s="FAAAAAAAAAAAAAAAA"'%test) if globals()[test]("FAAAAAAAAAAAAAAAA") != False: print globals()[test]("FAAAAAAAAAAAAAAAA") raise AssertionError 

在我的机器上(OS-X 10.5.8,core2duo,python2.7.3)用这些人为的(短的)string, str.count吸烟setall ,并且敲击str.translate一点,但是被str.translate strmul目前处于领先地位:

 WITH ALL EQUAL test_all 5.83863711357 test_count 0.947771072388 test_set 2.01028490067 test_replace 1.24682998657 test_translate 0.941282987595 test_strmul 0.629556179047 test_regex 2.52913498878 WITH FIRST NON-EQUAL test_all 2.41147494316 test_count 0.942595005035 test_set 2.00480484962 test_replace 0.960338115692 test_translate 0.924381017685 test_strmul 0.622269153595 test_regex 1.36632800102 

在不同的系统和不同的string之间,时间可能会稍微(或甚至显着)不同,所以值得用实际的string进行研究。

最后,如果你足够的最好的情况下,你的string足够长,你可能要考虑一个。 这是一个更好的algorithm…我会避免set解决scheme,因为我没有看到任何可能击败count解决scheme的情况。

如果内存可能是一个问题,那么需要避免str.translatestr.translatestrmul因为那些string会创build第二个string,但这些通常不是现在的问题。

你可以转换成一个集合,并检查只有一个成员:

 len(set("AAAAAAAA")) 

尝试使用内置函数all

 all(c == 'A' for c in s) 

为此问题添加另一个解决scheme

 >>> not "AAAAAA".translate(None,"A") True 

如果您需要检查string中的所有字符是否相同并且等于给定字符,则需要删除所有重复项,并检查最终结果是否等于单个字符。

 >>> set("AAAAA") == set("A") True 

如果你想查找是否有任何重复,只需检查长度

 >>> len(set("AAAAA")) == 1 True 

有趣的答案到目前为止。 这是另一个:

 flag = True for c in 'AAAAAAAfAAAA': if not c == 'A': flag = False break 

我能想到的唯一的好处是,如果发现不一致的字符,它不需要遍历整个string。

 not len("AAAAAAAAA".replace('A', ''))