【问题标题】:Fastest way to convert '(-1,0)' into tuple(-1, 0)?将“(-1,0)”转换为元组(-1,0)的最快方法?
【发布时间】:2009-10-24 20:19:14
【问题描述】:

我有一个巨大的字符串元组,它们是从程序返回的。返回的示例元组可能如下所示:

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

可以将这些字符串转换为真正的元组(里面有整数),但我希望有人知道一个很好的技巧来加快这个速度。我想出的任何东西都感觉像是在以一种相对“缓慢”的方式进行。正如我所提到的,这些列表可能很大,因此非常感谢快速方法!

谢谢

编辑一个 好吧,看起来 eval 是一种较慢的方法。但到目前为止,我已经测试了 4 种方法,感谢任何 cmets 和提交! :)

另外,有人问我元组的大小。它的范围从几个到不超过几百万。不是“太大”,但足够大,速度是一个重要因素。我不是来进行微优化的,只是学习任何我可能不知道的新技巧。例如,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

【问题讨论】:

  • 那么,你做了什么,为什么你认为它很慢?
  • 我马上就会发布我的测试。我也会用任何其他想法来更新它,但到目前为止我不“知道”我的速度很慢,但我希望可能有一些漂亮的技巧。 Python 经常出人头地,所以我一直在尝试学习新方法:)
  • 由于字符串输入元组的长度似乎是一个因素,请将您的 test_tuple 分配行更改为test_tuple = ('(-1,0)',etc.,etc.)*100
  • 任何时候我看到这种模式:returnlist = []; for item in sequence: returnlist.append(...something...); return returnlist,就像你在 b1 中看到的那样,你应该考虑改为列表理解。 returnlist = [...something... for item in sequence] 将更快地构建已解析的元组列表,因为它在 C 代码中进行 for 迭代,而不是在 Python 代码中进行迭代。

标签: python string tuples


【解决方案1】:

我根本不建议您使用 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

【讨论】:

  • 虽然您的方法肯定更快,但由于时间开销,您的比例似乎有所偏差。针对我的百万元组列表,您的方法需要 6.08 秒,而 eval 需要 16.02 秒,因此方法上的差异在这里很重要。
  • @Jed,我在发布数字之前重复了几次测试。但是比例的不同可能是因为目标列表长度的不同。
  • 我也有同样的怀疑,我觉得对于这样的问题,对长度为“n”的大列表进行操作比在小列表上重复测试“n”次更好
  • 好点,也许我应该重做试妆?虽然我更喜欢在处理好大列表和处理好小列表之间的中间立场。平均列表大小可能在几千左右,但很难说。如果某些东西处理百万个项目元组优于其他东西,那么在其中放置一个列表大小检查以两种不同的方式处理 sizez 可能是值得的。
  • 在一个很小的列表上运行“n”次测试的麻烦在于您要花费大量时间来初始化测试框架。测试框架对您的数字的干扰越少越好。
【解决方案2】:
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

对于 1,000,000 个不是 的元组(但也不是 很好)。开销大概是使用eval 解析 Python 100 万次。但是,这是完成您所追求的最简单的方法。

使用列表理解而不是 map 的答案与我的 try/except 案例一样慢(本身很有趣):

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

话虽如此,我要冒险的是,过早的优化在这里起作用——解析字符串总是很慢。你期望有多少个元组?

【讨论】:

    【解决方案3】:

    如果你知道格式,我会做字符串解析。比 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
    

    【讨论】:

    • 呵呵,你做了我所做的,除了你用一个映射到 int 来加速它。喜欢它,我会把它添加到列表中:)
    【解决方案4】:

    我的电脑比 Nadia 的慢,但它运行得更快

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

    使用列表推导更快

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

    【讨论】:

    • 您的函数(尤其是第一个函数)似乎是此处介绍的所有函数中最快的。如果有人想知道,我在 Intel Core 2 Duo E6400 上使用 python-2.6-9.fc11.x86_64。
    【解决方案5】:

    如果您确定输入格式正确:

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

    【讨论】:

    • +1。在这种情况下,您必须确定 tuples 字符串来自受信任的来源。否则可能是安全威胁。
    【解决方案6】:

    您可以使用 YAPPS 快速启动和运行解析器。

    【讨论】:

    • 我将不得不调查 YAPPS,甚至不知道它存在。感谢您的信息:)
    【解决方案7】:

    您可以使用 yaml 或 json 为您解析成元组。

    【讨论】:

    • 他必须将 {} 添加到字符串中,json 才能工作(并且限制自己将 simplejson 作为鸡蛋安装或要求 Python 2.6 用于他的应用程序)。
    • +1。为了加快速度,您可以切换到非常快的cjson
    【解决方案8】:
    import ast
    
    list_of_tuples = map(ast.literal_eval, tuple_of_strings)
    

    【讨论】:

      猜你喜欢
      • 2017-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-29
      • 1970-01-01
      • 2014-02-14
      相关资源
      最近更新 更多