Наскоро се сблъсках със следния казус:
Как най-ефективно да преобразувам списък от repr на двойки елементи в списък от двойки елементи (т.е. да eval-на всеки string в python-ски tuple). Пример:
["('text1', 'text2')", "('text3', 'text4')", ... "('textn', 'textm')"]
Ето и четирите начина, които тествах:
from time import time
import re
def time_it(n, func, data):
start = time()
out = map(func, data)
print 'time for case %d:' % n, time()-start
return out
data = map(repr, zip(map(str, range(10**6)), map(str, range(10**6))))
out = []
# 1 - eval на всеки елемент
out.append(time_it(1, eval, data))
# 2 - премахване на ненужните символи
out.append(time_it(2, lambda x: x[2:-2].split('\', \''), data))
# 3 - match-ване на стойностите с регулярен израз за всеки елемент
pattern = re.compile(r'''\('(\w+)', '(\w+)'\)''')
out.append(time_it(3, lambda x: pattern.search(x).group(1, 2), data))
# 4 - match-ване на стойностите с регулярен израз върху всички елементи обединени в един string
pattern = re.compile(r"'(\w+)'")
start = time()
res = pattern.findall(''.join(data))
out.append(zip(res[::2], res[1::2]))
print 'time for case 4:', time()-start
print 'are they all the same?', len(filter(lambda o: o != out[0], map(lambda x: map(tuple, x), out))) == 0
Ето и времената:
time for case 1: 15.3229999542
time for case 2: 2.14499998093
time for case 3: 1.76999998093
time for case 4: 1.20600008965
are they all the same? True
Познах им класацията по бързодействие още преди да ги напиша
.
Естествено, най-правилният начин е този, който първи идва в главата на човек – eval. За съжаление обаче, ще трябва доста да почакате! Дори и да се направите на хитри и да пробвате със следния код (който е още по-грозен от вариант номер 4)
eval(repr(data).replace('"', ''))
пак времето нужно за изпълнение е около 10 пъти повече от вариант номер 4.
Макар че четвъртият пример е най-ефективен, не го препоръчвам поради ред причини, но най-вече защото кодът не се ръководи по форматa на данните, а просто агрегира всичко, което е между кавички. За това пък третият вариант е доста близък по време до четвъртия и за разлика от него се ръководи по структурата на данните, не прави един огромен текстов низ и е доста по-кратък и разбираем. Вариант номер 2 го забравете, че съществува
Та, в заключение – гледайте да използвате по-рядко eval и да балансирате добре между разбираемост и бързодействие.

P.S. Eval is Evil by default… so never use it for evaluating external code or else your program will execute someone else’s code
Тагове: benchmark, hacks, Python