2010-07-29 13 views
6

Si me gustaría realizar varias sustituciones de cadenas, ¿cuál es la forma más eficiente de llevarlo a cabo?Efectuar múltiples reemplazos de cadenas de forma eficiente en Python

Un ejemplo de la clase de situación que he encontrado en mis viajes es el siguiente:

>>> strings = ['a', 'list', 'of', 'strings'] 
>>> [s.replace('a', '')...replace('u', '') for s in strings if len(s) > 2] 
['a', 'lst', 'of', 'strngs'] 
+0

No estoy seguro de cómo piensa utilizar una tabla de búsqueda: ¿cuáles son las claves y los valores? Además, su ejemplo tiene algunos errores tipográficos/incoherencias. – zdav

+0

¿Dónde están los errores tipográficos? He usado puntos suspensivos para truncar el ejemplo de legibilidad. –

Respuesta

9

El ejemplo específico que da (borrar caracteres individuales) es perfecto para el método translate de cadenas, como lo es la sustitución de caracteres individuales con caracteres únicos. Si la cadena de entrada es una de Unicode, entonces, además de los dos tipos anteriores de "sustitución", la sustitución de caracteres individuales con cadenas de caracteres múltiples también está bien con el método translate (aunque si necesita trabajar con cadenas de bytes))

Si necesita reemplazar subcadenas de múltiples caracteres, entonces yo también recomendaría el uso de una expresión regular - aunque no en la forma en que la respuesta de @ gnibbler recomienda; más bien, construiría la expresión regular desde r'onestring|another|yetanother|orthis' (unir las subcadenas que deseas reemplazar con barras verticales - asegúrate también de incluirlas en re.escape si contienen caracteres especiales, por supuesto) y escribir una función de sustitución simple basada en un dict.

No voy a ofrecer mucho código en este momento ya que no sé cuál de los dos párrafos se aplica a sus necesidades reales, pero (cuando más tarde regrese a casa y verifique SO nuevamente ;-) Estaré encantado de editar para agregar un ejemplo de código según sea necesario dependiendo de sus modificaciones a su pregunta (más útil que los comentarios a esta respuesta ;-).

Editar: en un comentario el PO dice que quiere una respuesta "más general" (sin aclarar lo que eso significa) a continuación, en una edición de su Q dice que quiere estudiar las "compensaciones" entre diversos fragmentos todos de los cuales usan subcadenas de un solo carácter (y comprueban su presencia, en lugar de reemplazar como se solicitó originalmente, una semántica completamente diferente, por supuesto).

Dada esta total y completa confusión, todo lo que puedo decir es que para "controlar las compensaciones" (rendimiento) me gusta usar python -mtimeit -s'setup things here' 'statements to check' (asegurándome de que las afirmaciones para verificar no tienen efectos secundarios para evitar distorsiones en las mediciones de tiempo, timeit realiza bucles implícitamente para proporcionar mediciones precisas de tiempo).

una respuesta general (sin ningún tipo de ventajas y desventajas, y la participación de subseries de varios caracteres, de modo completamente contrario a su edición de Q pero consonantes a sus comentarios - los dos es totalmente contradictorio que es, por supuesto, imposible de cumplir ambos):

import re 

class Replacer(object): 

    def __init__(self, **replacements): 
    self.replacements = replacements 
    self.locator = re.compile('|'.join(re.escape(s) for s in replacements)) 

    def _doreplace(self, mo): 
    return self.replacements[mo.group()] 

    def replace(self, s): 
    return self.locator.sub(self._doreplace, s) 

ejemplo uso:

r = Replacer(zap='zop', zip='zup') 
print r.replace('allazapollezipzapzippopzip') 

Si algunas de las subcadenas que ser reemplazadas son palabras clave de Python, que necesitan para ser aprobada en un poco menos directamente, por ejemplo,, Lo siguiente:

r = Replacer(abc='xyz', def='yyt', ghi='zzq') 

fallarían porque def es una palabra clave, por lo que necesita ej .:

r = Replacer(abc='xyz', ghi='zzq', **{'def': 'yyt'}) 

o similares.

Me parece un buen uso para una clase (en lugar de programación de procedimiento) porque el RE para localizar las subcadenas a reemplazar, el dict expresando con qué reemplazarlas, y el método que realiza el reemplazo, realmente claman ser "mantenidos todos juntos", y una instancia de clase es la forma correcta de realizar dicho "mantenimiento conjunto" en Python. Una fábrica de cierre también podría funcionar (ya que el método replace es realmente la única parte de la instancia que necesidades sean visibles "fuera"), pero en una posiblemente menos clara, más difícil de depurar manera:

def make_replacer(**replacements): 
    locator = re.compile('|'.join(re.escape(s) for s in replacements)) 

    def _doreplace(mo): 
    return replacements[mo.group()] 

    def replace(s): 
    return locator.sub(_doreplace, s) 

    return replace 

r = make_replacer(zap='zop', zip='zup') 
print r('allazapollezipzapzippopzip') 

La única ventaja real podría ser un rendimiento modestamente mejor (debe verificarse con timeit en "casos de referencia" considerados significativos y representativos para la aplicación que lo usa) como el acceso a las "variables libres" (replacements, locator, _doreplace) en este caso podría ser minuciosamente más rápido que el acceso a los nombres calificados (self.replacements, etc.) en el enfoque normal basado en la clase (si esto es el caso dependerá de la implementación de Python en uso, de ahí la necesidad de verificar con timeit en puntos de referencia significativos!).

+0

Gracias por su respuesta detallada Alex. De hecho, estaba buscando una respuesta más general. Perdón por no estar claro con la pregunta. Editaré la Q para reflejar eso. –

0

Usted puede encontrar que es más rápido para crear una expresión regular y hacer todas las sustituciones a la vez.

también una buena idea para mover el código de sustitución a una función para que pueda memoize si es probable que tengan duplicados en la lista

>>> import re 
>>> [re.sub('[aeiou]','',s) for s in strings if len(s) > 2] 
['a', 'lst', 'of', 'strngs'] 


>>> def replacer(s, memo={}): 
... if s not in memo: 
...  memo[s] = re.sub('[aeiou]','',s) 
... return memo[s] 
... 
>>> [replacer(s) for s in strings if len(s) > 2] 
['a', 'lst', 'of', 'strngs'] 
+0

¿Eres capaz de ampliar esto? ¿'Re.sub ('[aeiou]', '', s)' reemplazaría todo al mismo tiempo? Si está revisando char por char, estaba preocupado por la inmutabilidad de la cadena de Python. –

+0

@Tim, Sí, '[aeiou]' reemplaza todas las vocales a la vez. La inmutabilidad no es un problema ya que estás creando nuevas cadenas. –

+0

@Tim Los bloqueos '[]' son una clase de caracteres, coincide con cualquiera de ellos y reemplaza ese carácter con la cadena de reemplazo. Esta solución es agradable si: todos los reemplazos son iguales, y si solo está combinando caracteres únicos. – zdav

Cuestiones relacionadas