самый быстрый способ сравнить строки в 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 команд здесь, и тестовая строка соответствует каждой из них, чтобы показать, что даже в экстремальных обстоятельствах у вас все еще могут быть сотни пользователей, делающих это, и они никогда не увидят никакого отставания.