pyplot python regex string performance replace

pyplot - title plt python



Reemplazo de cadena masiva en python? (13)

Aquí está el acercamiento de las extensiones de C para el pitón

const char *dvals[]={ //"0-64 "","","","","","","","","","", "","","","","","","","","","", "","","","","","","","","","", "","","","","","","","","","", "","","","","","","","","","", "","","","","","","","","","", "","","","","", //A-Z "","","","","", "","","","","", "","","","","", "","","","","", "","","","","33", "", // "","","","","","", //a-z "","32","31","","", "","","","","", "","","","","", "","","","","", "34","","","","30", "" }; int dsub(char*d,char*s){ char *ofs=d; do{ if(*s==''&'' && s[1]<=''z'' && *dvals[s[1]]){ ///033[0; *d++=''//',*d++=''0'',*d++=''3'',*d++=''3'',*d++=''['',*d++=''0'',*d++='';''; //consider as fixed 2 digits *d++=dvals[s[1]][0]; *d++=dvals[s[1]][1]; *d++=''m''; s++; //skip //non &,invalid, unused (&) ampersand sequences will go here. }else *d++=*s; }while(*s++); return d-ofs-1; }

Códigos de Python que he probado

from mylib import * import time start=time.time() instr="The &yquick &cbrown &bfox &Yjumps over the &ulazy dog, skip &Unknown./n"*100000 x=dsub(instr) end=time.time() print "time taken",end-start,",input str length",len(x) print "first few lines" print x[:1100]

Resultados

time taken 0.140000104904 ,input str length 11000000 first few lines The /033[0;30mquick /033[0;31mbrown /033[0;32mfox /033[0;33mjumps over the /033[0;34mlazy dog, skip &Unknown. The /033[0;30mquick /033[0;31mbrown /033[0;32mfox /033[0;33mjumps over the /033[0;34mlazy dog, skip &Unknown. The /033[0;30mquick /033[0;31mbrown /033[0;32mfox /033[0;33mjumps over the /033[0;34mlazy dog, skip &Unknown. The /033[0;30mquick /033[0;31mbrown /033[0;32mfox /033[0;33mjumps over the /033[0;34mlazy dog, skip &Unknown. The /033[0;30mquick /033[0;31mbrown /033[0;32mfox /033[0;33mjumps over the /033[0;34mlazy dog, skip &Unknown. The /033[0;30mquick /033[0;31mbrown /033[0;32mfox /033[0;33mjumps over the /033[0;34mlazy dog, skip &Unknown. The /033[0;30mquick /033[0;31mbrown /033[0;32mfox /033[0;33mjumps over the /033[0;34mlazy dog, skip &Unknown. The /033[0;30mquick /033[0;31mbrown /033[0;32mfox /033[0;33mjumps over the /033[0;34mlazy dog, skip &Unknown. The /033[0;30mquick /033[0;31mbrown /033[0;32mfox /033[0;33mjumps over the /033[0;34mlazy dog, skip &Unknown. The /033[0;30mquick /033[0;31mbrown /033[0;32mfox /033[0;33mjumps over the /033[0;34mlazy dog, skip &Unknown.

Se supone que puede ejecutarse en O (n) , y Only took 160 ms (avg) para una cadena de 11 MB en My Mobile Celeron 1.6 GHz PC

También omitirá los caracteres desconocidos tal como está, por ejemplo &Unknown volverá como está

Avísame si tienes algún problema con la compilación, errores, etc.

Digamos que tengo una cadena que se ve así:

str = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog"

Notarás muchas ubicaciones en la cadena donde hay un signo y seguido de un carácter (como "y y" y "& c"). Necesito reemplazar estos caracteres con un valor apropiado que tengo en un diccionario, así:

dict = {"&y":"/033[0;30m", "&c":"/033[0;31m", "&b":"/033[0;32m", "&Y":"/033[0;33m", "&u":"/033[0;34m"}

¿Cuál es la forma más rápida de hacer esto? Pude encontrar manualmente todos los símbolos y luego recorrer el diccionario para cambiarlos, pero parece lento. Hacer un montón de regex reemplaza parece lento también (tendré un diccionario de aproximadamente 30-40 pares en mi código actual).

Cualquier sugerencia es apreciada, gracias.

Editar:

Como se ha señalado en los comentarios de esta pregunta, mi diccionario se define antes del tiempo de ejecución y nunca cambiará durante el ciclo de vida de las aplicaciones. Es una lista de secuencias de escape de ANSI, y tendrá alrededor de 40 elementos. Mi longitud promedio de cadena para comparar será de unos 500 caracteres, pero habrá unos de hasta 5000 caracteres (aunque estos serán raros). También estoy usando Python 2.6 actualmente.

Editar # 2. Acepté la respuesta de Tor Valamos como la correcta, ya que no solo daba una solución válida (aunque no era la mejor ), sino que tomaba en cuenta a todos los demás e hizo una gran cantidad de trabajo para compararlos a todos. . Esa respuesta es una de las mejores y más útiles respuestas que he encontrado en StackOverflow. Felicitaciones a usted.


Aquí hay una versión usando split / join

mydict = {"y":"/033[0;30m", "c":"/033[0;31m", "b":"/033[0;32m", "Y":"/033[0;33m", "u":"/033[0;34m"} mystr = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog" myparts = mystr.split("&") myparts[1:]=[mydict[x[0]]+x[1:] for x in myparts[1:]] print "".join(myparts)

En caso de que existan signos de unión con códigos inválidos, puede usar esto para conservarlos

myparts[1:]=[mydict.get(x[0],"&"+x[0])+x[1:] for x in myparts[1:]]

Peter Hansen señaló que esto falla cuando hay doble ampersand. En ese caso usa esta versión

mystr = "The &yquick &cbrown &bfox &Yjumps over the &&ulazy dog" myparts = mystr.split("&") myparts[1:]=[mydict.get(x[:1],"&"+x[:1])+x[1:] for x in myparts[1:]] print "".join(myparts)


Como alguien mencionó el uso de un analizador simple, pensé que cocinaría uno usando pyparsing. Al utilizar el método transformString de pyparsing, pyparsing escanea internamente la cadena fuente y crea una lista del texto coincidente y el texto intermedio. Cuando todo está hecho, transformString then '''' .join es esta lista, por lo que no hay problemas de rendimiento en la creación de cadenas por incrementos. (La acción de análisis definida para ANSIreplacer hace la conversión de los caracteres coincidentes & _ a la secuencia de escape deseada, y reemplaza el texto coincidente con el resultado de la acción de análisis sintáctico. Dado que solo las secuencias coincidentes satisfarán la expresión del analizador, no es necesario que analizar acción para manejar secuencias indefinidas y _).

FollowedBy (''&'') no es estrictamente necesario, pero abrevia el proceso de análisis verificando que el analizador esté en realidad en signo y antes de hacer la comprobación más costosa de todas las opciones de marcado.

from pyparsing import FollowedBy, oneOf escLookup = {"&y":"/033[0;30m", "&c":"/033[0;31m", "&b":"/033[0;32m", "&Y":"/033[0;33m", "&u":"/033[0;34m"} # make a single expression that will look for a leading ''&'', then try to # match each of the escape expressions ANSIreplacer = FollowedBy(''&'') + oneOf(escLookup.keys()) # add a parse action that will replace the matched text with the # corresponding ANSI sequence ANSIreplacer.setParseAction(lambda toks: escLookup[toks[0]]) # now use the replacer to transform the test string; throw in some extra # ampersands to show what happens with non-matching sequences src = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog & &Zjumps back" out = ANSIreplacer.transformString(src) print repr(out)

Huellas dactilares:

''The /x1b[0;30mquick /x1b[0;31mbrown /x1b[0;32mfox /x1b[0;33mjumps over the /x1b[0;34mlazy dog & &Zjumps back''

Esto definitivamente no ganará ningún concurso de rendimiento, pero si su margen de beneficio comienza a ser más complicado, entonces tener una base de analizador facilitará la extensión.


El problema al hacer esta sustitución masiva en Python es la inmutabilidad de las cadenas: cada vez que reemplazas un elemento en la cadena, se reasignará una cadena nueva y otra vez desde el montón.

Entonces, si quiere la solución más rápida, necesita usar un contenedor mutable (por ejemplo, una lista) o escribir esta maquinaria en la C simple (o mejor en Pyrex o Cython). En cualquier caso, sugeriría escribir un analizador simple basado en una máquina de estado finito simple, y alimentar símbolos de su cadena uno por uno.

Soluciones sugeridas basadas en expresiones regulares que funcionan de manera similar, porque la expresión regular funciona usando fsm detrás de la escena.


Parece que hace lo que quiere: reemplazar varias cadenas a la vez usando RegExps. Aquí está el código relevante:

def multiple_replace(dict, text): # Create a regular expression from the dictionary keys regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys()))) # For each match, look-up corresponding value in dictionary return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text) print multiple_replace(dict, str)


Pruebe esto, haciendo uso de la sustitución de expresiones regulares y el formato de cadenas estándar:

# using your stated values for str and dict: >>> import re >>> str = re.sub(r''(&[a-zA-Z])'', r''%(/1)s'', str) >>> str % dict ''The /x1b[0;30mquick /x1b[0;31mbrown /x1b[0;32mfox /x1b[0;33mjumps over the /x1b[0;34mlazy dog''

La llamada re.sub () reemplaza todas las secuencias de signos y seguido de una sola letra con el patrón% (..) s que contiene el mismo patrón.

El formato% aprovecha una característica del formato de cadena que puede tomar un diccionario para especificar la sustitución, en lugar de los argumentos posicionales más comunes.

Una alternativa puede hacer esto directamente en el re.sub, utilizando una devolución de llamada:

>>> import re >>> def dictsub(m): >>> return dict[m.group()] >>> str = re.sub(r''(&[a-zA-Z])'', dictsub, str)

Esta vez estoy usando un cierre para hacer referencia al diccionario desde dentro de la función de devolución de llamada. Este enfoque podría darle un poco más de flexibilidad. Por ejemplo, podría usar algo como dict.get(m.group(), ''??'') para evitar generar excepciones si tuviera cadenas con secuencias de código no reconocidas.

(Por cierto, tanto "dict" como "str" ​​son funciones integradas, y te meterás en problemas si usas esos nombres en tu propio código. Solo en caso de que no lo sepas. Están bien para una pregunta como esta, por supuesto).

Editar: decidí verificar el código de prueba de Tor y concluí que no es para nada representativo y, de hecho, problemático. La cadena generada ni siquiera tiene signos de unión (!). El siguiente código revisado genera un diccionario representativo y una cadena, similar a las entradas de ejemplo del OP.

También quería verificar que la salida de cada algoritmo fuera la misma. A continuación se muestra un programa de prueba revisado, con solo Tor''s, el mío y el código de Claudiu, porque los otros estaban interrumpiendo la entrada de muestra. (Creo que todos son frágiles a menos que el diccionario traduzca básicamente todas las secuencias de caracteres y secuencias posibles, lo que estaba haciendo el código de prueba de Tor). Este siembra correctamente el generador de números aleatorios para que cada ejecución sea la misma. Finalmente, agregué una variación menor usando un generador que evita una sobrecarga de llamada de función, para una mejora de rendimiento menor.

from time import time import string import random import re random.seed(1919096) # ensure consistent runs # build dictionary with 40 mappings, representative of original question mydict = dict((''&'' + random.choice(string.letters), ''/x1b[0;%sm'' % (30+i)) for i in range(40)) # build simulated input, with mix of text, spaces, ampersands in reasonable proportions letters = string.letters + '' '' * 12 + ''&'' * 6 mystr = ''''.join(random.choice(letters) for i in range(1000)) # How many times to run each solution rep = 10000 print(''Running %d times with string length %d and %d ampersands'' % (rep, len(mystr), mystr.count(''&''))) # Tor Valamo # fixed from Tor''s test, so it actually builds up the final string properly t = time() for x in range(rep): output = mystr for k, v in mydict.items(): output = output.replace(k, v) print(''%-30s'' % ''Tor fixed & variable dict'', time() - t) # capture "known good" output as expected, to verify others expected = output # Peter Hansen # build charset to use in regex for safe dict lookup charset = ''''.join(x[1] for x in mydict.keys()) # grab reference to method on regex, for speed patsub = re.compile(r''(&[%s])'' % charset).sub t = time() for x in range(rep): output = patsub(r''%(/1)s'', mystr) % mydict print(''%-30s'' % ''Peter fixed & variable dict'', time()-t) assert output == expected # Peter 2 def dictsub(m): return mydict[m.group()] t = time() for x in range(rep): output = patsub(dictsub, mystr) print(''%-30s'' % ''Peter fixed dict'', time() - t) assert output == expected # Peter 3 - freaky generator version, to avoid function call overhead def dictsub(d): m = yield None while 1: m = yield d[m.group()] dictsub = dictsub(mydict).send dictsub(None) # "prime" it t = time() for x in range(rep): output = patsub(dictsub, mystr) print(''%-30s'' % ''Peter generator'', time() - t) assert output == expected # Claudiu - Precompiled regex_sub = re.compile("(%s)" % "|".join(mydict.keys())).sub t = time() for x in range(rep): output = regex_sub(lambda mo: mydict[mo.string[mo.start():mo.end()]], mystr) print(''%-30s'' % ''Claudio fixed dict'', time() - t) assert output == expected

Olvidé incluir resultados de referencia antes:

Running 10000 times with string length 1000 and 96 ampersands (''Tor fixed & variable dict '', 2.9890000820159912) (''Peter fixed & variable dict '', 2.6659998893737793) (''Peter fixed dict '', 1.0920000076293945) (''Peter generator '', 1.0460000038146973) (''Claudio fixed dict '', 1.562000036239624)

Además, fragmentos de las entradas y salida correcta:

mystr = ''lTEQDMAPvksk k&z Txp vrnhQ GHaO&GNFY&&a...'' mydict = {''&p'': ''/x1b[0;37m'', ''&q'': ''/x1b[0;66m'', ''&v'': ...} output = ''lTEQDMAPvksk k←[0;57m Txp vrnhQ GHaO←[0;67mNFY&&a P...''

Comparando con lo que vi de la salida del código de prueba de Tor:

mystr = ''VVVVVVVPPPPPPPPPPPPPPPXXXXXXXXYYYFFFFFFFFFFFFEEEEEEEEEEE...'' mydict = {''&p'': ''112'', ''&q'': ''113'', ''&r'': ''114'', ''&s'': ''115'', ...} output = # same as mystr since there were no ampersands inside


Si el número de teclas en la lista es grande, y el número de ocurrencias en la cadena es bajo (y mayormente cero), entonces podría iterar sobre las ocurrencias de los símbolos en la cadena, y usar el diccionario marcado por el primero carácter de las subcadenas. No codigo a menudo en Python así que el estilo puede estar un poco apagado, pero aquí está mi opinión:

str = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog" dict = {"&y":"/033[0;30m", "&c":"/033[0;31m", "&b":"/033[0;32m", "&Y":"/033[0;33m", "&u":"/033[0;34m"} def rep(s): return dict["&"+s[0:1]] + s[1:] subs = str.split("&") res = subs[0] + "".join(map(rep, subs[1:])) print res

Por supuesto, hay una pregunta sobre qué sucede cuando hay un signo comercial que proviene de la cadena en sí, tendría que escapar de alguna manera antes de alimentar a través de este proceso, y luego deshacer el paisaje después de este proceso.

Por supuesto, como es bastante habitual con los problemas de rendimiento, es oportuno cronometrar los diversos enfoques en su conjunto de datos típico (y también el peor de los casos) y compararlos.

EDITAR: colocarlo en una función separada para trabajar con diccionario arbitrario:

def mysubst(somestr, somedict): subs = somestr.split("&") return subs[0] + "".join(map(lambda arg: somedict["&" + arg[0:1]] + arg[1:], subs[1:]))

EDIT2: deshacerse de una concatenación innecesaria, parece ser aún un poco más rápido que el anterior en muchas iteraciones.

def mysubst(somestr, somedict): subs = somestr.split("&") return subs[0].join(map(lambda arg: somedict["&" + arg[0:1]] + arg[1:], subs[1:]))


Si realmente quiere profundizar en el tema, eche un vistazo a esto: http://en.wikipedia.org/wiki/Aho-Corasick_algorithm

La solución obvia al iterar sobre el diccionario y reemplazar cada elemento de la cadena toma el tiempo O(n*m) , donde n es el tamaño del diccionario, m es la longitud de la cadena.

Mientras que el Algoritmo Aho-Corasick encuentra todas las entradas del diccionario en O(n+m+f) donde f es el número de elementos encontrados.


Tampoco estoy seguro de la velocidad de esta solución, pero podría recorrer su diccionario y llamar repetidamente al

str.replace(old, new)

Esto podría funcionar decentemente bien si la secuencia original no es demasiado larga, pero obviamente sufriría a medida que la cadena se alarga.


Una solución general para definir reglas de reemplazo es usar la sustitución de re.sub() regulares usando una función para proporcionar el mapa (vea re.sub() ).

import re str = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog" dict = {"&y":"/033[0;30m", "&c":"/033[0;31m", "&b":"/033[0;32m", "&Y":"/033[0;33m", "&u":"/033[0;34m"} def programmaticReplacement( match ): return dict[ match.group( 1 ) ] colorstring = re.sub( ''(/&.)'', programmaticReplacement, str )

Esto es particularmente bueno para sustituciones no triviales (por ejemplo, cualquier cosa que requiera operaciones matemáticas para crear el sustituto).


prueba esto

tr.replace("&y",dict["&y"])

tr.replace("&c",dict["&c"])

tr.replace("&b",dict["&b"])

tr.replace("&Y",dict["&Y"])

tr.replace("&u",dict["&u"])


>>> a=[] >>> str = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog" >>> d={"&y":"/033[0;30m", ... "&c":"/033[0;31m", ... "&b":"/033[0;32m", ... "&Y":"/033[0;33m", ... "&u":"/033[0;34m"} >>> for item in str.split(): ... if item[:2] in d: ... a.append(d[item[:2]]+item[2:]) ... else: a.append(item) >>> print '' ''.join(a)


mydict = {"&y":"/033[0;30m", "&c":"/033[0;31m", "&b":"/033[0;32m", "&Y":"/033[0;33m", "&u":"/033[0;34m"} mystr = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog" for k, v in mydict.iteritems(): mystr = mystr.replace(k, v) print mystr The ←[0;30mquick ←[0;31mbrown ←[0;32mfox ←[0;33mjumps over the ←[0;34mlazy dog

Me tomé la libertad de comparar algunas soluciones:

mydict = dict([(''&'' + chr(i), str(i)) for i in list(range(65, 91)) + list(range(97, 123))]) # random inserts between keys from random import randint rawstr = ''''.join(mydict.keys()) mystr = '''' for i in range(0, len(rawstr), 2): mystr += chr(randint(65,91)) * randint(0,20) # insert between 0 and 20 chars from time import time # How many times to run each solution rep = 10000 print ''Running %d times with string length %d and '' / ''random inserts of lengths 0-20'' % (rep, len(mystr)) # My solution t = time() for x in range(rep): for k, v in mydict.items(): mystr.replace(k, v) #print(mystr) print ''%-30s'' % ''Tor fixed & variable dict'', time()-t from re import sub, compile, escape # Peter Hansen t = time() for x in range(rep): sub(r''(&[a-zA-Z])'', r''%(/1)s'', mystr) % mydict print ''%-30s'' % ''Peter fixed & variable dict'', time()-t # Claudiu def multiple_replace(dict, text): # Create a regular expression from the dictionary keys regex = compile("(%s)" % "|".join(map(escape, dict.keys()))) # For each match, look-up corresponding value in dictionary return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text) t = time() for x in range(rep): multiple_replace(mydict, mystr) print ''%-30s'' % ''Claudio variable dict'', time()-t # Claudiu - Precompiled regex = compile("(%s)" % "|".join(map(escape, mydict.keys()))) t = time() for x in range(rep): regex.sub(lambda mo: mydict[mo.string[mo.start():mo.end()]], mystr) print ''%-30s'' % ''Claudio fixed dict'', time()-t # Andrew Y - variable dict def mysubst(somestr, somedict): subs = somestr.split("&") return subs[0] + "".join(map(lambda arg: somedict["&" + arg[0:1]] + arg[1:], subs[1:])) t = time() for x in range(rep): mysubst(mystr, mydict) print ''%-30s'' % ''Andrew Y variable dict'', time()-t # Andrew Y - fixed def repl(s): return mydict["&"+s[0:1]] + s[1:] t = time() for x in range(rep): subs = mystr.split("&") res = subs[0] + "".join(map(repl, subs[1:])) print ''%-30s'' % ''Andrew Y fixed dict'', time()-t

Resultados en Python 2.6

Running 10000 times with string length 490 and random inserts of lengths 0-20 Tor fixed & variable dict 1.04699993134 Peter fixed & variable dict 0.218999862671 Claudio variable dict 2.48400020599 Claudio fixed dict 0.0940001010895 Andrew Y variable dict 0.0309998989105 Andrew Y fixed dict 0.0310001373291

Las soluciones de claudiu y andrew se mantuvieron en 0, así que tuve que aumentarlo a 10 000 ejecuciones.

Lo ejecuté en Python 3 (debido a Unicode) con reemplazos de caracteres de 39 a 1024 (38 es ampersand, así que no quería incluirlo). Longitud de cadena de hasta 10,000 incluyendo aproximadamente 980 reemplazos con insertos aleatorios variables de longitud 0-20. Los valores Unicode de 39 a 1024 causan caracteres de 1 y 2 bytes de longitud, lo que podría afectar algunas soluciones.

mydict = dict([(''&'' + chr(i), str(i)) for i in range(39,1024)]) # random inserts between keys from random import randint rawstr = ''''.join(mydict.keys()) mystr = '''' for i in range(0, len(rawstr), 2): mystr += chr(randint(65,91)) * randint(0,20) # insert between 0 and 20 chars from time import time # How many times to run each solution rep = 10000 print(''Running %d times with string length %d and '' / ''random inserts of lengths 0-20'' % (rep, len(mystr))) # Tor Valamo - too long #t = time() #for x in range(rep): # for k, v in mydict.items(): # mystr.replace(k, v) #print(''%-30s'' % ''Tor fixed & variable dict'', time()-t) from re import sub, compile, escape # Peter Hansen t = time() for x in range(rep): sub(r''(&[a-zA-Z])'', r''%(/1)s'', mystr) % mydict print(''%-30s'' % ''Peter fixed & variable dict'', time()-t) # Peter 2 def dictsub(m): return mydict[m.group()] t = time() for x in range(rep): sub(r''(&[a-zA-Z])'', dictsub, mystr) print(''%-30s'' % ''Peter fixed dict'', time()-t) # Claudiu - too long #def multiple_replace(dict, text): # # Create a regular expression from the dictionary keys # regex = compile("(%s)" % "|".join(map(escape, dict.keys()))) # # # For each match, look-up corresponding value in dictionary # return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text) # #t = time() #for x in range(rep): # multiple_replace(mydict, mystr) #print(''%-30s'' % ''Claudio variable dict'', time()-t) # Claudiu - Precompiled regex = compile("(%s)" % "|".join(map(escape, mydict.keys()))) t = time() for x in range(rep): regex.sub(lambda mo: mydict[mo.string[mo.start():mo.end()]], mystr) print(''%-30s'' % ''Claudio fixed dict'', time()-t) # Separate setup for Andrew and gnibbler optimized dict mydict = dict((k[1], v) for k, v in mydict.items()) # Andrew Y - variable dict def mysubst(somestr, somedict): subs = somestr.split("&") return subs[0] + "".join(map(lambda arg: somedict[arg[0:1]] + arg[1:], subs[1:])) def mysubst2(somestr, somedict): subs = somestr.split("&") return subs[0].join(map(lambda arg: somedict[arg[0:1]] + arg[1:], subs[1:])) t = time() for x in range(rep): mysubst(mystr, mydict) print(''%-30s'' % ''Andrew Y variable dict'', time()-t) t = time() for x in range(rep): mysubst2(mystr, mydict) print(''%-30s'' % ''Andrew Y variable dict 2'', time()-t) # Andrew Y - fixed def repl(s): return mydict[s[0:1]] + s[1:] t = time() for x in range(rep): subs = mystr.split("&") res = subs[0] + "".join(map(repl, subs[1:])) print(''%-30s'' % ''Andrew Y fixed dict'', time()-t) # gnibbler t = time() for x in range(rep): myparts = mystr.split("&") myparts[1:]=[mydict[x[0]]+x[1:] for x in myparts[1:]] "".join(myparts) print(''%-30s'' % ''gnibbler fixed & variable dict'', time()-t)

Resultados:

Running 10000 times with string length 9491 and random inserts of lengths 0-20 Tor fixed & variable dict 0.0 # disqualified 329 secs Peter fixed & variable dict 2.07799983025 Peter fixed dict 1.53100013733 Claudio variable dict 0.0 # disqualified, 37 secs Claudio fixed dict 1.5 Andrew Y variable dict 0.578000068665 Andrew Y variable dict 2 0.56299996376 Andrew Y fixed dict 0.56200003624 gnibbler fixed & variable dict 0.530999898911

(** Tenga en cuenta que el código de gnibbler utiliza un dict diferente, donde las claves no tienen el ''&'' incluido. El código de Andrew también usa esta dict alternativa, pero no hizo mucha diferencia, tal vez solo una aceleración de 0.01x).