将'(-1,0)'快速转换为元组(-1, 0)的最快方法是什么?

1 投票
8 回答
517 浏览
提问于 2025-04-15 15:20

我有一个很大的字符串元组,这些字符串是从一个程序返回的。返回的元组示例如下:

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

可以把这些字符串转换成真正的元组(里面有整数),但我希望有人能告诉我一个快速的方法来加速这个过程。我想到的办法都感觉比较“慢”。而且,正如我提到的,这些列表可能很大,所以一个快速的方法会非常受欢迎!

谢谢大家

编辑一 好吧,看来使用eval的方法比较慢。不过到目前为止,我已经测试了4种方法,感谢大家的评论和建议!:)

还有,有人问我的元组有多大。它的大小从几个到希望不超过几百万不等。虽然不算“太”大,但足够大,速度是一个重要因素。我不是来做微优化的,只是想学习一些我可能不知道的新技巧。例如,eval()是我经常忘记的东西,尽管在这种情况下似乎效果不太好。

编辑二 我还想说明一下,字符串的格式不应该改变。所以不需要检查格式。此外,这是嵌入的Python v2.6.2,所以任何需要 2.6的都可以。至于3.0,就不太行了;)

大家的建议都很棒,再次感谢大家的参与!:)

编辑三 还有一点。我注意到我返回的代码并没有产生一个“元组”,这没关系,如果有人认为最终结果“必须”是元组,那我很抱歉。类似格式的结果也是可以的。

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

8 个回答

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
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

对于一百万个元组来说,这个速度算不上(但也不算)。这里的额外开销,可能是因为使用eval解析Python代码一百万次。不过,这确实是实现你想要的功能最简单的方法。

使用列表推导而不是map的方法,速度和我尝试的try/except方法差不多(这本身就挺有意思的):

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

话虽如此,我觉得这里可能存在过早优化的问题——解析字符串总是比较慢。你预计会有多少个元组呢?

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

撰写回答