这已经得到了令人满意的回答,但是对于事后遇到这个问题的人,我已经对几种不同的方法进行了一些概要分析。在我的情况下,我想要大写的十六进制数字,所以根据需要进行修改以满足您的需要。
这是我的测试实现:
import re
hex_digits = set("ABCDEF1234567890")
hex_match = re.compile(r'^[A-F0-9]+\Z')
hex_search = re.compile(r'[^A-F0-9]')
def test_set(input):
return set(input) <= hex_digits
def test_not_any(input):
return not any(c not in hex_digits for c in input)
def test_re_match1(input):
return bool(re.compile(r'^[A-F0-9]+\Z').match(input))
def test_re_match2(input):
return bool(hex_match.match(input))
def test_re_match3(input):
return bool(re.match(r'^[A-F0-9]+\Z', input))
def test_re_search1(input):
return not bool(re.compile(r'[^A-F0-9]').search(input))
def test_re_search2(input):
return not bool(hex_search.search(input))
def test_re_search3(input):
return not bool(re.match(r'[^A-F0-9]', input))
以及测试,在 Mac OS X 上的 Python 3.4.0 中:
import cProfile
import pstats
import random
# generate a list of 10000 random hex strings between 10 and 10009 characters long
# this takes a little time; be patient
tests = [ ''.join(random.choice("ABCDEF1234567890") for _ in range(l)) for l in range(10, 10010) ]
# set up profiling, then start collecting stats
test_pr = cProfile.Profile(timeunit=0.000001)
test_pr.enable()
# run the test functions against each item in tests.
# this takes a little time; be patient
for t in tests:
for tf in [test_set, test_not_any,
test_re_match1, test_re_match2, test_re_match3,
test_re_search1, test_re_search2, test_re_search3]:
_ = tf(t)
# stop collecting stats
test_pr.disable()
# we create our own pstats.Stats object to filter
# out some stuff we don't care about seeing
test_stats = pstats.Stats(test_pr)
# normally, stats are printed with the format %8.3f,
# but I want more significant digits
# so this monkey patch handles that
def _f8(x):
return "%11.6f" % x
def _print_title(self):
print(' ncalls tottime percall cumtime percall', end=' ', file=self.stream)
print('filename:lineno(function)', file=self.stream)
pstats.f8 = _f8
pstats.Stats.print_title = _print_title
# sort by cumulative time (then secondary sort by name), ascending
# then print only our test implementation function calls:
test_stats.sort_stats('cumtime', 'name').reverse_order().print_stats("test_*")
结果如下:
50335004 次函数调用在 13.428 秒内
排序依据:累计时间、函数名
由于限制,名单从 20 个减少到 8 个
ncalls tottime percall cumtime percall filename:lineno(function)
10000 0.005233 0.000001 0.367360 0.000037 :1(test_re_match2)
10000 0.006248 0.000001 0.378853 0.000038 :1(test_re_match3)
10000 0.010710 0.000001 0.395770 0.000040 :1(test_re_match1)
10000 0.004578 0.000000 0.467386 0.000047 :1(test_re_search2)
10000 0.005994 0.000001 0.475329 0.000048 :1(test_re_search3)
10000 0.008100 0.000001 0.482209 0.000048 :1(test_re_search1)
10000 0.863139 0.000086 0.863139 0.000086:1(测试集)
10000 0.007414 0.000001 9.962580 0.000996:1(test_not_any)
在哪里:
- 通话
- 该函数被调用的次数
- 总时间
- 在给定函数中花费的总时间,不包括子函数的时间
- 电话
- tottime 除以 ncalls 的商
- 业余时间
- 在此和所有子功能中花费的累积时间
- 电话
- cumtime 除以原始调用的商
我们真正关心的列是 cumtime 和 percall,因为它向我们展示了从函数进入到退出的实际时间。正如我们所看到的,正则表达式匹配和搜索并没有太大的不同。
如果您每次都编译正则表达式,那么不用费心编译它会更快。编译一次比每次快 7.5%,但编译比不编译快 2.5%。
test_set 比 re_search 慢两倍,比 re_match 慢三倍
test_not_any 比 test_set 慢了一个数量级
TL;DR:使用 re.match 或 re.search