2009-10-24 4 views
1

J'ai un grand nombre de chaînes, qui sont renvoyées d'un programme. Un exemple tuple retourné pourrait ressembler à ceci:Le moyen le plus rapide de convertir '(-1,0)' en tuple (-1, 0)?

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

Je peux convertir ces cordes à tuples réelles (avec des entiers à l'intérieur), mais j'espère que quelqu'un connaît une belle astuce pour accélérer ce. Tout ce que je suis venu avec se sent comme je le fais d'une manière relativement «lente». Et comme je l'ai mentionné, ces listes peuvent être grandes, donc un moyen rapide serait très apprécié!

Merci

modifier un Bon, son semblant que eval est une méthode plus lente de le faire. Mais jusqu'à présent, j'ai testé 4 méthodes, merci pour vos commentaires et soumissions! :)

Aussi, quelqu'un a demandé sur la taille de mes tuples. Il va varier de quelques-uns, pour ne pas dépasser quelques millions. Pas "trop" gros, mais assez gros pour que la vitesse soit un facteur important. Je ne suis pas ici pour micro-optimiser, juste apprendre de nouveaux trucs astucieux dont je ne suis pas au courant. Par exemple, eval() est quelque chose que je oublie souvent, même si cela ne semble pas très bien dans ce cas.

modifier deux Je voulais aussi noter que le format de la chaîne ne devrait pas changer. Donc pas besoin de vérifier le format. En outre, ceci est un Python v2.6.2 embarqué, donc n'importe quoi exigeant 2.6 est bien. 3.0 d'autre part, non pas tant;)

recherche grand gars, encore une fois, merci pour l'entrée :)

modifier 3 Encore une autre note. J'ai remarqué que j'avais retourné du code qui ne résultait pas en un "tuple", c'est correct, et désolé si quelqu'un pensait que le résultat final "devait" être un tuple. Quelque chose de format similaire est bien.

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) 

Résultats dans:

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

Alors, qu'avez-vous fait, et pourquoi pensez-vous qu'il est lent? –

+0

Je poste mes tests dans une seconde. Je vais aussi le mettre à jour avec d'autres idées, mais pour l'instant, je ne sais pas si le mien est lent, mais j'avais espéré qu'il y aurait un truc astucieux. Python tire souvent un rabit d'un chapeau, donc j'essaie toujours d'apprendre de nouvelles façons :) –

+0

Puisque la longueur du tuple d'entrée de chaînes semble être un facteur, changez votre ligne d'affectation test_tuple à 'test_tuple = ('(-1,0)', etc., etc.) * 100'. – PaulMcG

Répondre

1

Si vous êtes sûr que l'entrée est bien formé:

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

+1. Dans ce cas, vous devez être sûr que la chaîne 'tuples' provient d'une source fiable. Sinon, cela pourrait constituer une menace pour la sécurité. –

+0

-1 pour ne pas utiliser 'map'. Je dois utiliser ces builtins. :) –

1

Vous pouvez obtenir un analyseur et en cours d'exécution assez rapidement avec Yapps.

+0

Je vais devoir regarder dans YAPPS, je ne savais même pas qu'il existait. Merci pour l'info :) –

3
map(eval, tuples) 

Cela ne tient pas compte du cas où l'un des n-uplets n'est pas syntaxiquement correct. Pour cela, je vous recommande quelque chose comme:

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

map(do, tuples) 

Les deux méthodes testées pour la vitesse:

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

Pour 1000000 tuples qui est pas mal (mais pas grand non plus). L'overhead, probablement, est en analysant un million de fois Python en utilisant eval. Cependant, c'est la façon la plus simple de faire ce que vous cherchez.

La réponse en utilisant la compréhension de la liste au lieu de map est à peu près aussi lent que mon essai/sauf cas (intéressant en lui-même):

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

Tout cela étant dit, je vais risquer l'optimisation prématurée est à travailler ici - l'analyse des chaînes est toujours lente. Combien de tuples attendez-vous?

1

vous pouvez simplement utiliser yaml ou json pour l'analyser en tuples pour vous.

+0

Il devrait ajouter {{} 'à la chaîne pour que' json' fonctionne (et se limiter à installer simplejson comme un oeuf ou exiger Python 2.6 pour son application). –

+0

+1. Pour accélérer cela, vous pouvez passer à 'cjson' qui est terriblement rapide. –

2

Je ferais l'analyse de chaîne si vous connaissez le format. Plus rapide que 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 

si vous avez besoin ints

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

Hehe, vous avez fait exactement ce que j'ai fait, sauf que vous l'avez accéléré avec une carte à int. J'adore ça, je vais ajouter ça à la liste :) –

10

Je ne conseille pas d'utiliser eval du tout. C'est lent et peu sûr. Vous pouvez le faire:

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

Les chiffres parlent d'eux-mêmes:

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

Alors que votre méthode est définitivement plus rapide, vos proportions semblent décalées en raison des frais généraux de temps. Votre méthode prend 6,08 secondes contre ma liste de million-tuple, et eval prend 16,02 secondes, donc la différence de méthodologie est importante ici. –

+0

@Jed, j'ai répété le test plusieurs fois avant de poster les numéros.Mais la différence dans les proportions pourrait être due à la différence dans la longueur de la liste cible. –

+0

Je soupçonne la même chose, et je me sens comme opérant sur une grande liste de longueur 'n' est mieux pour une question comme celle-ci au lieu de répéter le test sur une petite liste 'n' fois –

2

Mon ordinateur est plus lent que de Nadia, mais cela va plus vite

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

que ce

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

en utilisant une liste compr ehension est plus rapide encore

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

Vos fonctions (surtout la première) semblent être les plus rapides de toutes les fonctions présentées ici. En cas de doute, j'utilise python-2.6-9.fc11.x86_64 sur un Intel Core 2 Duo E6400. –

0
import ast 

list_of_tuples = map(ast.literal_eval, tuple_of_strings) 
Questions connexes