С chars_array = ['a', 'e', 'i', 'o', 'u', 'y']
и words
, равными списку
из 56048 английских слов я измерил несколько вариантов с помощью команды, аналогичной следующей в приглашении IPython:
%timeit n = [version1(word) for word in words]
В каждом случае сообщалось «10 циклов, лучшее из 3», и я показал время на цикл
в комментариях рядом с каждым определением функции ниже:
# OP's originals:
def version1(word): # 163 ms
chars =[x for x in word]
count = 0
for c in chars:
if not c in chars_array:
count+=1
return count
def version2(word): # 173 ms
return sum([1 for c in [x for x in word] if not c in chars_array])
Теперь давайте нажмем version1
и version2
с тремя оптимизациями:
- удалить избыточное понимание списка и вместо этого перебрать
word
;
- используйте оператор
not in
вместо отрицания результата оператора in
;
- проверка на (не) членство в
set
, а не list
.
_
chars_set = set(chars_array)
def version1a(word): # 95.5 ms
count = 0
for c in word:
if c not in chars_set:
count+=1
return count
def version2a(word): # 104 ms
return sum([1 for c in word if c not in chars_set])
Так что на самом деле многострочный код имеет преимущество перед пониманием списка. Это может зависеть от длины слова, хотя: version2a
должен выделить новый список такой же длины, что и слово, тогда как version1a
- нет. Давайте уточним version2a
далее, чтобы дать ему то же преимущество, суммируя по выражению генератора, а не по списку:
def version2b(word): # 111 ms
return sum(1 for c in word if c not in chars_set)
К моему удивлению, это было на самом деле немного контрпродуктивно, но опять же, этот эффект может зависеть от длины слова.
Наконец, давайте почувствуем силу .translate()
:
chars_str = ''.join(chars_set)
def version3(word): # 40.7 ms
return len(word.translate(None, chars_str))
У нас явный победитель.