在python中比较字符串的最快方法
Posted
技术标签:
【中文标题】在python中比较字符串的最快方法【英文标题】:fastest way to compare strings in python 【发布时间】:2011-01-04 11:11:27 【问题描述】:我正在用 Python 编写一个脚本,该脚本将允许用户输入一个字符串,该字符串将是一个指示脚本执行特定操作的命令。为了争论,我会说我的命令列表是:
lock
read
write
request
log
现在,我希望用户能够输入“日志”这个词,它会执行一个特定的操作,这很简单。但是,我想匹配部分单词。因此,例如,如果用户输入“lo”,它应该匹配“lock”,因为它在列表中较高。我已经尝试使用 libc 中的 strncmp 使用 ctypes 来完成此操作,但还没有做出正面或反面。
【问题讨论】:
速度到底有多重要?假设这是在用户输入命令时运行一次,并运行一小组命令(少于 1000 个),即使是最低效(实用)的实现也会在一毫秒内返回——这对于用户。 这是一个在 Twisted 框架上运行的网络应用程序,最多可能有 50 个用户同时输入命令,因此如果所有 50 个用户都输入命令并且我正在输入命令,则可能会有延迟解析它们的效率低下。 twisted 是线程化的。你仍然不会注意到任何影响。大多数计算机可以在您的手指按下一个键的时间内比较 10,000 个或更多的字符串。这称为过早优化,您是在把时间浪费在琐碎的事情上。 简单构建,然后测量性能问题所在。似乎不太可能是命令解析。 @SpliFF 我同意消息的精神,但是,Twisted 没有线程。至少,除非你这样做(例如 deferToThread)。 【参考方案1】:如果您接受来自用户的输入,那么您为什么担心比较的速度?即使是最慢的技术也会比用户感知的快得多。尽可能使用最简单、最易理解的代码,并将效率问题留给紧密的内部循环。
cmds = [
"lock",
"read",
"write",
"request",
"log",
]
def match_cmd(s):
matched = [c for c in cmds if c.startswith(s)]
if matched:
return matched[0]
【讨论】:
希望我能投票 10 倍。这就是我如此热爱 UI 开发的原因——时间尺度绝对(嗯,相对)巨大。您可以悠闲地花 100 毫秒做某事而没有人注意到。【参考方案2】:这会做你想做的事:
def select_command(commands, user_input):
user_input = user_input.strip().lower()
for command in commands:
if command.startswith(user_input):
return command
return None
但是:
你似乎对错误的事情过度担心了。所以 50 个用户意味着 50 毫秒——你不会因为那种“滞后”而跑出城外。担心数据库访问效率低下或用户在认为会收到“请求”时键入“r”并获得“读取”而导致的问题。在 1960 年代将用户击键风险降至最低,这并不好笑。他们在用什么? ASR33 电传打字机?至少你可以坚持一个独特的匹配——“rea”表示读取,“req”表示请求。
【讨论】:
【参考方案3】:这已按照您的要求在运行时进行了优化...(尽管很可能不需要)
这里有一段简单的代码,它将获取映射到函数的命令输入字典,并生成映射到同一函数的所有非重复子命令的输出字典。
因此,您在启动服务时运行它,然后您将获得 100% 优化的查找。我相信有更聪明的方法可以做到这一点,所以请随意编辑。
commands =
'log': log_function,
'exit': exit_function,
'foo': foo_function,
'line': line_function,
cmap =
kill = set()
for command in commands:
for pos in range(len(1,command)):
subcommand = command[0:pos]
if subcommand in cmap:
kill.add(subcommand)
del(cmap[subcommand])
if subcommand not in kill:
cmap[subcommand] = commands[command]
#cmap now is the following - notice the duplicate prefixes removed?
'lo': log_function,
'log': log_function,
'e': exit_function,
'ex': exit_function,
'exi': exit_function,
'exit': exit_function,
'f' : foo_function,
'fo' : foo_function,
'foo' : foo_function,
'li' : line_function,
'lin' : line_function,
'line' : line_function,
【讨论】:
【参考方案4】:你可以使用startswith
例如
myword = "lock"
if myword.startswith("lo"):
print "ok"
或者如果您想在单词中找到“lo”,无论位置如何,只需使用“in”运算符
if "lo" in myword
因此,有一种方法可以做到这一点:
for cmd in ["lock","read","write","request","log"]:
if cmd.startswith(userinput):
print cmd
break
【讨论】:
@ghostdog74,最好阅读更多详细信息:“我想匹配部分单词。因此,例如,如果用户输入“lo”,它应该匹配“lock”,因为它在列表。” 彼得汉森是正确的。需要匹配部分单词以使系统更易于使用。我将(最终)有一些复杂的命令,并且能够将它们缩写为单个字母非常方便。【参考方案5】:我建议你看看使用 readline python 库,而不是重新发明***。 用户必须点击 tab 才能完成单词,但您可以设置 readline 以便 tab 尽可能匹配或循环遍历所有以当前存根开头的单词。
这似乎是对 python http://www.doughellmann.com/PyMOTW/readline/index.html 中 readline 的一个相当不错的介绍
【讨论】:
【参考方案6】:python-Levenshtein 中的jaro_winkler()
可能就是您要查找的内容。
【讨论】:
【参考方案7】:这是改编自J.Tauber's Trie implementation in Python,您可以比较和/或重新调整您需要的任何额外功能。另请参阅Wikipedia entry on tries。
class Trie:
def __init__(self):
self.root = [None, ]
def add(self, key):
curr_node = self.root
for ch in key:
curr_node = curr_node[1].setdefault(ch, [key, ])
curr_node[0] = key
def find(self, key):
curr_node = self.root
for ch in key:
try:
curr_node = curr_node[1][ch]
except KeyError:
return None
return curr_node[0]
设置(添加顺序很重要!):
t = Trie()
for word in [
'lock',
'read',
'write',
'request',
'log']:
t.add(word)
然后这样调用:
>>> t.find('lo')
'lock'
>>> t.find('log')
'log'
>>> t.find('req')
'request'
>>> t.find('requiem')
>>>
【讨论】:
不开玩笑,但另一个“使用startswith() 遍历列表”似乎真的无聊。 ;-) 至少我的 Stratswith 努力解决了 OP 的(毫无意义的)效率问题,通过在第一场比赛中退出 :-) @John,啊,所以你认为如果“lo”也出现在列表中,但在“lock”之后,输入“lo”将返回“lock”作为匹配项是可以接受的吗?我没想到。 @Peter Hansen:本月 +1 非 sequitur。你没有理由认为我认为这是可以接受的。这显然是不能接受的。然而,这是OP所说的他想要的自然结果。他显然知道列表中的顺序很重要。人们需要相信(a)他不会在“lock”或“log”之后放置“lo”(b)如果失败,他会测试他的代码并发现用户无法访问“lo”功能。 @John,当我的解决方案没有遇到问题时,这几乎不是“自然结果”。我还会注意到,“在第一场比赛中保释”虽然整体解决方案仍然较慢,因为它遍历一长串命令,这并不是解决性能问题的好方法。 (当然,在我们通过讨论完成任何事情之前,我们需要更多关于性能需求和实际列表大小的背景知识。)【参考方案8】:如果我正确理解您的 Q,您需要一个 sn-p,它会在得到答案后立即返回,而无需进一步遍历您的“命令列表”。这应该做你想做的:
from itertools import ifilter
def check_input(some_string, code_book) :
for q in ifilter(code_book.__contains__, some_string) :
return True
return False
【讨论】:
【参考方案9】:替换为您最喜欢的字符串比较功能。相当快,而且切中要害。
matches = ( x for x in list if x[:len(stringToSearchFor)] == stringToSearchFor )
print matches[0]
【讨论】:
(1) 见http://docs.python.org/library/stdtypes.html#str.startswith
(2) 不要使用list
;它会影响内置的list()
。【参考方案10】:
import timeit
cmds = []
for i in range(1,10000):
cmds.append("test")
def get_cmds(user_input):
return [c for c in cmds if c.startswith(user_input)]
if __name__=='__main__':
t = timeit.Timer("get_cmds('te')", "from __main__ import get_cmds")
print "%0.3f seconds" % (t.timeit(number=1))
#>>> 0.008 seconds
所以基本上,根据我的评论,您问的是如何优化不需要可测量时间或 CPU 的操作。我在这里使用了 10,000 个命令,测试字符串匹配每个命令,只是为了表明即使在极端情况下,您仍然可以有数百名用户执行此操作,而且他们永远不会看到任何延迟。
【讨论】:
以上是关于在python中比较字符串的最快方法的主要内容,如果未能解决你的问题,请参考以下文章
在 Python 中增加日期字符串 YYYY-MM-DD 的最快方法是啥?