76

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

all_equal("AAAAA", "A") = True

all_equal("AAAAAAAAAAA", "A") = True

all_equal("AAAAAfAAAAA", "A") = False

两种看似低效的方法是:首先将字符串转换为列表并检查每个元素,或者使用正则表达式。有没有更有效的方法,或者这些方法是 Python 中最好的方法吗?谢谢。

4

8 回答 8

130

这是迄今为止最快的,甚至比甚至快几倍count(),只需使用出色的 mgilson 计时套件进行计时

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

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

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

字符串越长,时间奖励就越大。但是,正如 mgilson 所写,它会创建字符串的副本,因此如果您的字符串长度是数百万个符号,则可能会成为问题。

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

UPD:关于空字符串的情况。如何处理它很大程度上取决于任务。如果任务是“检查字符串中的所有符号是否相同”,s == len(s) * s[0]则为有效答案(无符号表示错误,异常正常)。如果任务是“检查是否只有一个唯一符号”,空字符串应该给我们 False,答案是s and s == len(s) * s[0],或者bool(s) and s == len(s) * s[0]如果您更喜欢接收布尔值。最后,如果我们将任务理解为“检查是否有不同的符号”,空字符串的结果为 True,答案为not s or s == len(s) * s[0]

于 2013-01-14T15:46:49.850 回答
47
>>> s = 'AAAAAAAAAAAAAAAAAAA'
>>> s.count(s[0]) == len(s)
True

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

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

但是,我有一种感觉,由于优化的 C 实现,非短路版本可能会在某些字符串上表现更好(取决于大小等)


这是一个简单的timeit脚本,用于测试发布的其他一些选项:

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)上,带有这些人为的(短)字符串,str.count冒烟setall,并且节拍str.replace一点,但被淘汰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

不同系统和不同字符串的时间可能略有不同(甚至显着?),因此值得研究您计划传递的实际字符串。

最终,如果你足够多地达到最佳情况all,并且你的琴弦足够长,你可能想要考虑那个。这是一个更好的算法......我会避免set解决方案,因为我没有看到任何可能击败count解决方案的情况。

如果内存可能是一个问题,您将需要避免str.translatestr.replace并且strmul因为它们会创建第二个字符串,但这些天这通常不是问题。

于 2013-01-14T15:04:01.807 回答
17

您可以转换为一组并检查只有一个成员:

len(set("AAAAAAAA"))
于 2013-01-14T15:03:25.333 回答
13

尝试使用内置函数all

all(c == 'A' for c in s)
于 2013-01-14T15:03:00.223 回答
6

为这个问题添加另一个解决方案

>>> not "AAAAAA".translate(None,"A")
True
于 2013-01-14T15:26:33.063 回答
5

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

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

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

>>> len(set("AAAAA")) == 1
True
于 2013-01-14T15:03:46.693 回答
3

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

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

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

于 2013-01-14T15:14:04.087 回答
2
not len("AAAAAAAAA".replace('A', ''))
于 2013-01-14T15:15:17.117 回答