2013-04-18 5 views
2

Сейчас я использую список для хранения измененной строки и возвращает строку с .join()Строка производительность конкатенация

def applyCoder(text, coder): 
    l = [] 
    for i in text: 
     if i in coder: 
      l.append(coder[i]) 
     else: 
      l.append(i) 
    return ''.join(l) 

# example output, shifts all letters by 3 in a string 
# print applyCoder("Hello, world!", buildCoder(3)) 
# buildCoder(3) returns dictionary, e.g. {'A': 'D', ...} 
# >>> Khoor, zruog! 

Есть ли более быстрый способ изменения и возвращает строку?

+0

Fix yo ur 'return' statement – jamylak

+0

сделал это опечатка – user2144553

ответ

4

Это должно быть как можно быстрее:

''.join([coder[i] if i in coder else i for i in text]) 

описаний списков гораздо более оптимизированы в Python по сравнению с for петлями, которые несут большую нагрузку. Я передал понимание списка в отличие от генератора в ''.join, потому что он должен знать длину его ввода заранее, прежде чем присоединяться. Если вы дадите ему генератор, он все равно должен сделать это в списке (что немного медленнее).

На самом деле вы можете упростить это еще дальше, который должен быть еще быстрее (это на самом деле работает медленнее, чем выше способом из-за вызова метода)

''.join([coder.get(i,i) for i in text]) 

Тайминги:

def applyCoder(text, coder): 
    L = [] 
    for i in text: 
     if i in coder: 
      L.append(coder[i]) 
     else: 
      L.append(i) 
    return ''.join(L) 

def list_comp(text, coder): 
    return ''.join([coder[i] if i in coder else i for i in text]) 

def list_comp2(text, coder): 
    return ''.join([coder.get(i,i) for i in text]) 

from timeit import timeit 
from string import ascii_letters 
d = dict(zip(ascii_letters, ascii_letters[3:] + ascii_letters[-3:])) 


print timeit(stmt='applyCoder("Hello, world!", d)', 
      setup='from __main__ import applyCoder, d;') 

print timeit(stmt='list_comp("Hello, world!", d)', 
      setup='from __main__ import list_comp, d;')  

print timeit(stmt='list_comp2("Hello, world!", d)', 
      setup='from __main__ import list_comp2, d;') 

print timeit(stmt='applyCoder("Hello, world!"*10, d)', 
      setup='from __main__ import applyCoder, d;') 

print timeit(stmt='list_comp("Hello, world!"*10, d)', 
      setup='from __main__ import list_comp, d;') 


print timeit(stmt='list_comp2("Hello, world!"*10, d)', 
      setup='from __main__ import list_comp2, d;') 

Результаты:

''' Test 1 ''' 
5.0159105417 # applyCoder 
3.41502481461 # listcomp1 
4.76796932292 # listcomp2 

''' Test 2 ''' 
34.9718502631 # applyCoder 
22.0451702661 # listcomp1 
34.1682597928 # listcomp2 

Похоже, что вызов метода coder.get полностью отрицает преимущества понимания списка. Я предсказал, что это может быть медленнее, чем listcomp1 из-за этого, но я не думал, что это сильно повлияет. В любом случае, понимание списка по-прежнему выигрывает.

Update: Если изменить list_comp2 так:

def list_comp2(text, coder): 
    coder_get = coder.get 
    return ''.join([coder_get(i,i) for i in text]) 

Времена резко улучшить:

из 4.76796932292 (первый тест) ->3.95217394948

и 34.1682597928 (2-й тест) ->27.1162974624

+0

+1 для красивого питонического кода. – Maroun

+0

Средний балл: 0,00015 против 0,00011 (ваш) с 10 000 звонков. – user2144553

+0

@ user2144553 Это должны быть крошечные входы, (я предполагаю, что это секунды), можете ли вы попробовать его на более крупных? Также, если вы могли бы рассказать мне о настройках обоих моих EDIT: я также попробую некоторые тайминги – jamylak

Смежные вопросы