2009-10-24 4 views
1

У меня есть огромный кортеж строк, которые возвращаются из программы. В качестве примера кортежа возвращается может выглядеть следующим образом:Самый быстрый способ конвертировать '(-1,0)' в кортеж (-1, 0)?

('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)') 

I может преобразовать эти строки в реальные кортежи (с целыми числами внутри), но я надеюсь, кто-нибудь знает хороший трюк, чтобы ускорить этот процесс. Все, что я придумал, похоже на то, что я делаю это относительно медленно. И, как я уже упоминал, эти списки могут быть большими, поэтому быстрый способ будет высоко оценен!

Благодаря

редактировать один Хорошо, так что его кажущаяся, что Eval более медленный способ сделать это. Но пока у меня есть 4 проверенных метода, спасибо за любые комментарии и комментарии! :)

Кроме того, кто-то спросил о размере моих кортежей. Он будет варьироваться от нескольких, и, надеюсь, не более нескольких миллионов. Не «слишком» большой, но достаточно большой, чтобы скорость была важным фактором. Я здесь не для микро-оптимизации, просто изучаю любые новые отличные трюки, о которых я, возможно, и не подозревал. Например, eval() - это то, о чем я часто забываю, хотя в этом случае это не так хорошо.

редактировать две Я также хотел бы отметить, что формат строки не должно изменяться. Поэтому нет необходимости проверять формат. Кроме того, это встроенный Python v2.6.2, поэтому ничего , требующее 2.6, в порядке. 3,0 с другой стороны, не так много;)

Глядя отличные ребята, опять же, спасибо за вход :)

редактировать 3 Еще одно замечание. Я заметил, что я возвращал код, который не приводил к «кортежу», это нормально, и жаль, если кто-то думал, что конечный результат «имел» быть кортежем. Что-то вроде формата прекрасно.

import timeit 

test_tuple = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)', '(7,0)',) 

def timeit_a(): 
    '''''' 
    def convert_tup_strings(tup_string): 
     first_int, last_int = tup_string[1:-1].split(',') 
     return (int(first_int), int(last_int)) 

    return map(convert_tup_strings, test_tuple) 

def timeit_a_1(): 
    '''''' 
    def convert_tup_strings(tup_string): 
     return map(int, tup_string[1:-1].split(',')) 

    return map(convert_tup_strings, test_tuple) 

def timeit_b(): 
    converted = [] 

    for tup_string in test_tuple: 
     first_int, last_int = tup_string[1:-1].split(',') 
     converted.append((int(first_int), int(last_int))) 

    return converted 

def timeit_b_1(): 
    converted = [] 

    for tup_string in test_tuple: 
     converted.append(map(int, tup_string[1:-1].split(','))) 

    return converted 

def timeit_c(): 
    '''''' 
    return [eval(t) for t in test_tuple] 

def timeit_d(): 
    '''''' 
    return map(eval, test_tuple) 

def timeit_e(): 
    '''''' 
    return map(lambda a: tuple(map(int, a[1:-1].split(','))), test_tuple) 

print 'Timeit timeit_a: %s' % timeit.timeit(timeit_a) 
print 'Timeit timeit_a_1: %s' % timeit.timeit(timeit_a_1) 
print 'Timeit timeit_b: %s' % timeit.timeit(timeit_b) 
print 'Timeit timeit_b_1: %s' % timeit.timeit(timeit_b_1) 
print 'Timeit timeit_c: %s' % timeit.timeit(timeit_c) 
print 'Timeit timeit_d: %s' % timeit.timeit(timeit_d) 
print 'Timeit timeit_e: %s' % timeit.timeit(timeit_e) 

Результаты в:

Timeit timeit_a: 15.8954099772 
Timeit timeit_a_1: 18.5484214589 
Timeit timeit_b: 15.3137666465 
Timeit timeit_b_1: 17.8405181116 
Timeit timeit_c: 91.9587832802 
Timeit timeit_d: 89.8858157489 
Timeit timeit_e: 20.1564312947 
+1

Итак, что вы сделали, и почему вы думаете, что это медленно? –

+0

Я отправляю свои тесты за секунду. Я также буду обновлять его любыми другими идеями, но до сих пор я не знаю, что у меня медленный, но я надеялся, что может быть какой-то отличный трюк. Python часто вытаскивает рати из шляпы, поэтому я всегда пытаюсь изучить новые пути :) –

+0

Поскольку длина входного кортежа строк, по-видимому, является фактором, измените вашу строку назначения test_tuple на 'test_tuple = ('(-1,0)', и т.д., и т.д.). * 100'. – PaulMcG

ответ

1

Если вы уверены, что вход хорошо образован:

tuples = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)') 
result = [eval(t) for t in tuples] 
+0

+1. В этом случае вы должны быть уверены, что строка 'tuples' поступает из надежного источника. В противном случае это может представлять угрозу безопасности. –

+0

-1 для использования 'map'. Должны использовать эти встроенные функции. :) –

1

Вы можете получить синтаксический анализатор и работает довольно быстро с YAPPS.

+0

Мне нужно будет изучить YAPPS, даже не подозревая, что он существует. Спасибо за info :) –

3
map(eval, tuples) 

Это не относится к случаю, когда один из кортежей не является синтаксически правильным. Для этого я бы рекомендовал что-то вроде:

def do(tup): 
    try: return eval(tup) 
    except: return None 

map(do, tuples) 

Оба метода тестируемых скорости:

>>> tuples = ["(1,0)"] * 1000000 

>>> # map eval 
>>> st = time.time(); parsed = map(eval, tuples); print "%.2f s" % (time.time() - st) 
16.02 s 

>>> # map do 
>>> >>> st = time.time(); parsed = map(do, tuples); print "%.2f s" % (time.time() - st) 
18.46 s 

Для 1000000 кортежей, что это не плохого (но это не большого либо). Накладные расходы, по-видимому, состоят в разборе Python один миллион раз, используя eval. Тем не менее, это самый простой способ сделать то, что вам нужно.

Ответ, используя список понимание вместо map примерно так же медленно, как мой Try/за исключением случая (интересно само по себе):

>>> st = time.time(); parsed = [eval(t) for t in tuples]; print "%.2f s" % (time.time() - st) 
18.13 s 

Все, что было сказано, что я собираюсь рисковать преждевременной оптимизации в работа здесь - синтаксические строки всегда медленны. Сколько кортежей вы ожидаете?

1

Вы можете просто использовать yaml или json, чтобы разобрать его в кортежи для вас.

+0

Он должен добавить '{}' в строку для 'json', чтобы работать (и ограничиться установкой simplejson в качестве яйца или требующим Python 2.6 для своего приложения). –

+0

+1. Чтобы ускорить это, вы можете переключиться на 'cjson', что очень быстро. –

2

Я бы сделал синтаксический анализ, если вы знаете формат. Быстрее, чем eval().

>>> tuples = ["(1,0)"] * 1000000 
>>> import time 
>>> st = time.time(); parsed = map(eval, tuples); print "%.2f s" % (time.time() - st) 
32.71 s 
>>> def parse(s) : 
... return s[1:-1].split(",") 
... 
>>> parse("(1,0)") 
['1', '0'] 
>>> st = time.time(); parsed = map(parse, tuples); print "%.2f s" % (time.time() - st) 
5.05 s 

, если вам нужно Интс

>>> def parse(s) : 
... return map(int, s[1:-1].split(",")) 
... 
>>> parse("(1,0)") 
[1, 0] 
>>> st = time.time(); parsed = map(parse, tuples); print "%.2f s" % (time.time() - st) 
9.62 s 
+0

Хе-хе, ты сделал именно то, что сделал, кроме того, что ты ускорил его с помощью карты в int. Полюбите его, я добавлю это в список :) –

10

Я не советую вам использовать Eval вообще. Он медленный и небезопасный. Вы можете сделать это:

result = map(lambda a: tuple(map(int, a[1:-1].split(','))), s) 

Цифры говорят сами за себя:

timeit.Timer("map(lambda a: tuple(map(int, a[1:-1].split(','))), s)", "s = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)')").timeit(100000) 

1.8787779808044434 

timeit.Timer("map(eval, s)", "s = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)')").timeit(100000) 

11.571426868438721 
+0

Хотя ваш метод определенно быстрее, ваши пропорции кажутся отключенными из-за накладных расходов timeit. Ваш метод занимает 6,08 секунды против моего списка из миллиона кортежей, а eval занимает 16,02 секунды, поэтому здесь важна разница в методологии. –

+0

@Jed, я повторил тест несколько раз, прежде чем публиковать номера.Но разница в пропорциях может быть связана с различием в длине списка целей. –

+0

Я бы заподозрил то же самое, и мне кажется, что работа над большим списком длины «n» лучше подходит для такого вопроса, вместо повторения теста в небольшом списке «n» times –

2

Мой компьютер работает медленнее, чем Надя, однако это работает быстрее

>>> timeit.Timer(
    "list((int(a),int(c)) for a,b,c in (x[1:-1].partition(',') for x in s))", 
    "s = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)')").timeit(100000) 
3.2250211238861084 

чем это

>>> timeit.Timer(
    "map(lambda a: tuple(map(int, a[1:-1].split(','))), s)", 
    "s = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)')").timeit(100000) 
3.8979239463806152 

с использованием списка ehension еще быстрее

>>> timeit.Timer(
    "[(int(a),int(c)) for a,b,c in (x[1:-1].partition(',') for x in s)]", 
    "s = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)')").timeit(100000) 
2.452484130859375 
+0

Ваши функции (особенно первая) кажутся самыми быстрыми из всех представленных здесь функций. В случае, если кто-то задается вопросом, я использую python-2.6-9.fc11.x86_64 на Intel Core 2 Duo E6400. –

0
import ast 

list_of_tuples = map(ast.literal_eval, tuple_of_strings) 
Смежные вопросы