самый быстрый способ сравнить строки в python
Я пишу скрипт на Python, который позволит пользователю вводить строку, которая будет командой, которая инструктирует скрипт выполнять определенное действие. Для аргументации я скажу, что мой список команд:
lock
read
write
request
log
теперь я хочу, чтобы пользователь мог ввести слово "log", и он выполнит определенное действие, которое очень просто. Однако я хотел бы сопоставить отдельные слова. Так, например, если пользователь вводит "lo" , он должен соответствовать "lock", так как он выше в список. Я попытался использовать strncmp из libc, используя ctypes для этого, но еще не сделал головы или хвосты.
10 ответов
Если вы принимаете ввод от пользователя, то почему вы беспокоитесь о скорости сравнения? Даже самая медленная техника будет намного быстрее, чем пользователь может воспринять. Используйте простейший наиболее понятный код, который вы можете, и оставьте проблемы эффективности для плотных внутренних циклов.
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]
это сделает то, что вы хотите:
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
:
Вы, кажется, overworried о плохом. Таким образом, 50 пользователей означает 50 миллисекунд-вы не будете выбегать из города для такого рода "отставания". Беспокойтесь о неэффективном доступе к базе данных или проблемах, вызванных пользователями, набравшими "r" и получившими "read", когда они думали, что получат "запрос". Минимизация пользовательских нажатий клавиш с риском ошибок настолько велика, что это не смешно. Что они используют? ASR33 телетайпы? По крайней мере, вы могли бы настоять на уникальном совпадении - "rea" для чтения и "req" для запроса.
это оптимизация во время выполнения, как вы просили... (хотя скорее всего не нужен)
вот простой бит кода, который будет принимать входной словарь команд, сопоставленных с функцией, и приводит к выходному словарю всех не повторяющихся суб-команд, сопоставленных с той же функцией.
таким образом, вы запускаете это при запуске службы, а затем у вас есть 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,
}
вы можете использовать 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
Я предлагаю вам посмотреть на использование библиотеки readline python, а не изобретать колесо. Пользователь должен будет нажать tab, чтобы завершить слово, но вы можете настроить readline так, чтобы вкладка соответствовала, насколько это возможно, или циклы через все слова, начиная с текущего заглушки.
Это, кажется, довольно приличное введение в readline в python http://www.doughellmann.com/PyMOTW/readline/index.html
jaro_winkler()
на питон-Левенштейна может быть то, что вы ищете.
Это взято из реализация Trie J. Tauber в Python, который вы можете сравнить и / или повторно адаптировать с любыми дополнительными функциями, которые вам нужны. См. также Википедия запись на пытается.
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')
>>>
Если я правильно понимаю ваш Q, вам нужен фрагмент, который вернет ответ, как только он его получит, не проходя дальше по вашему списку команд."Это должно сделать то, что вы хотите:
from itertools import ifilter
def check_input(some_string, code_book) :
for q in ifilter(code_book.__contains__, some_string) :
return True
return False
Replace с вашей любимой функцией сравнения строк. Довольно быстро, и по делу.
matches = ( x for x in list if x[:len(stringToSearchFor)] == stringToSearchFor )
print matches[0]
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
Итак, в основном, в моем комментарии Вы спрашиваете, как оптимизировать операцию, которая не требует измеримого времени или процессора. Я использовал 10 000 команд здесь, и тестовая строка соответствует каждой из них, чтобы показать, что даже в экстремальных обстоятельствах у вас все еще могут быть сотни пользователей, делающих это, и они никогда не увидят никакого отставания.