Python-самый быстрый способ проверить, содержит ли строка определенные символы в любом из элементов списка
каков самый быстрый способ проверить, содержит ли строка некоторые символы из любых элементов списка?
В настоящее время я использую этот метод:
lestring = "Text123"
lelist = ["Text", "foo", "bar"]
for x in lelist:
if lestring.count(x):
print 'Yep. "%s" contains characters from "%s" item.' % (lestring, x)
есть ли способ сделать это без итерации (что сделает его быстрее, я полагаю.)?
4 ответов
вы можете попробовать понимание списка с проверкой членства
>>> lestring = "Text123"
>>> lelist = ["Text", "foo", "bar"]
>>> [e for e in lelist if e in lestring]
['Text']
по сравнению с вашей реализацией, хотя LC имеет неявный цикл, но его быстрее, поскольку нет явного вызова функции, как в вашем случае с count
по сравнению с реализацией Джо, ваш путь быстрее, так как функция фильтра потребует вызова двух функций в цикле,lambda
и count
>>> def joe(lelist, lestring):
return ''.join(random.sample(x + 'b'*len(x), len(x)))
>>> def uz(lelist, lestring):
for x in lelist:
if lestring.count(x):
return 'Yep. "%s" contains characters from "%s" item.' % (lestring, x)
>>> def ab(lelist, lestring):
return [e for e in lelist if e in lestring]
>>> t_ab = timeit.Timer("ab(lelist, lestring)", setup="from __main__ import lelist, lestring, ab")
>>> t_uz = timeit.Timer("uz(lelist, lestring)", setup="from __main__ import lelist, lestring, uz")
>>> t_joe = timeit.Timer("joe(lelist, lestring)", setup="from __main__ import lelist, lestring, joe")
>>> t_ab.timeit(100000)
0.09391469893125759
>>> t_uz.timeit(100000)
0.1528471407273173
>>> t_joe.timeit(100000)
1.4272649857800843
прокомментированное решение Джейми медленнее для более короткого стринг. Вот результат теста
>>> def jamie(lelist, lestring):
return next(itertools.chain((e for e in lelist if e in lestring), (None,))) is not None
>>> t_jamie = timeit.Timer("jamie(lelist, lestring)", setup="from __main__ import lelist, lestring, jamie")
>>> t_jamie.timeit(100000)
0.22237164127909637
Если вам нужны логические значения, для более коротких строк просто измените приведенное выше выражение LC
[e in lestring for e in lelist if e in lestring]
или для более длинных строк, вы можете сделать следующее
>>> next(e in lestring for e in lelist if e in lestring)
True
или
>>> any(e in lestring for e in lelist)
filter(lambda x: lestring.count(x), lelist)
, что вернет все строки, которые вы пытаетесь найти в списке.
если тест должен увидеть, есть ли какие-либо общие символы (не слова или сегменты), создайте набор из букв в списке, а затем проверьте Буквы в строке:
char_list = set(''.join(list_of_words))
test_set = set(string_to_teat)
common_chars = char_list.intersection(test_set)
однако я предполагаю, что вы ищете всего лишь один общий символ...
на esmre библиотека делает трюк. В вашем случае, чем проще, esm (часть esmre) - это то, что вы хотите.
https://pypi.python.org/pypi/esmre/
https://code.google.com/p/esmre/
у них есть хорошая документация и примеры: Взято из их примеров:
>>> import esm
>>> index = esm.Index()
>>> index.enter("he")
>>> index.enter("she")
>>> index.enter("his")
>>> index.enter("hers")
>>> index.fix()
>>> index.query("this here is history")
[((1, 4), 'his'), ((5, 7), 'he'), ((13, 16), 'his')]
>>> index.query("Those are his sheep!")
[((10, 13), 'his'), ((14, 17), 'she'), ((15, 17), 'he')]
>>>
Я провел несколько тестов производительности:
import random, timeit, string, esm
def uz(lelist, lestring):
for x in lelist:
if lestring.count(x):
return 'Yep. "%s" contains characters from "%s" item.' % (lestring, x)
def ab(lelist, lestring):
return [e for e in lelist if e in lestring]
def use_esm(index, lestring):
return index.query(lestring)
for TEXT_LEN in [5, 50, 1000]:
for SEARCH_LEN in [5, 20]:
for N in [5, 50, 1000, 10000]:
if TEXT_LEN < SEARCH_LEN:
continue
print 'TEXT_LEN:', TEXT_LEN, 'SEARCH_LEN:', SEARCH_LEN, 'N:', N
lestring = ''.join((random.choice(string.ascii_uppercase + string.digits) for _ in range(TEXT_LEN)))
lelist = [''.join((random.choice(string.ascii_uppercase + string.digits) for _ in range(SEARCH_LEN))) for _
in range(N)]
index = esm.Index()
for i in lelist:
index.enter(i)
index.fix()
t_ab = timeit.Timer("ab(lelist, lestring)", setup="from __main__ import lelist, lestring, ab")
t_uz = timeit.Timer("uz(lelist, lestring)", setup="from __main__ import lelist, lestring, uz")
t_esm = timeit.Timer("use_esm(index, lestring)", setup="from __main__ import index, lestring, use_esm")
ab_time = t_ab.timeit(1000)
uz_time = t_uz.timeit(1000)
esm_time = t_esm.timeit(1000)
min_time = min(ab_time, uz_time, esm_time)
print ' ab%s: %f' % ('*' if ab_time == min_time else '', ab_time)
print ' uz%s: %f' % ('*' if uz_time == min_time else '', uz_time)
print ' esm%s %f:' % ('*' if esm_time == min_time else '', esm_time)
и получил, что результаты зависят в основном от количество элементов, которые вы ищете (в моем случае, 'N'):
TEXT_LEN: 1000 SEARCH_LEN: 20 N: 5
ab*: 0.001733
uz: 0.002512
esm 0.126853:
TEXT_LEN: 1000 SEARCH_LEN: 20 N: 50
ab*: 0.017564
uz: 0.023701
esm 0.079925:
TEXT_LEN: 1000 SEARCH_LEN: 20 N: 1000
ab: 0.370371
uz: 0.489523
esm* 0.133783:
TEXT_LEN: 1000 SEARCH_LEN: 20 N: 10000
ab: 3.678790
uz: 4.883575
esm* 0.259605: