Necesitaba una solución donde las cadenas a ser reemplazadas pueden ser expresiones regulares, por ejemplo, para ayudar a normalizar un texto largo reemplazando múltiples caracteres de espacios en blanco por uno solo. Partiendo de una cadena de respuestas de otros, incluidos MiniQuark y mmj, esto es lo que se me ocurrió:
def multiple_replace(string, reps, re_flags = 0):
""" Transforms string, replacing keys from re_str_dict with values.
reps: dictionary, or list of key-value pairs (to enforce ordering;
earlier items have higher priority).
Keys are used as regular expressions.
re_flags: interpretation of regular expressions, such as re.DOTALL
"""
if isinstance(reps, dict):
reps = reps.items()
pattern = re.compile("|".join("(?P<_%d>%s)" % (i, re_str[0])
for i, re_str in enumerate(reps)),
re_flags)
return pattern.sub(lambda x: reps[int(x.lastgroup[1:])][1], string)
Funciona para los ejemplos dados en otras respuestas, por ejemplo:
>>> multiple_replace("(condition1) and --condition2--",
... {"condition1": "", "condition2": "text"})
'() and --text--'
>>> multiple_replace('hello, world', {'hello' : 'goodbye', 'world' : 'earth'})
'goodbye, earth'
>>> multiple_replace("Do you like cafe? No, I prefer tea.",
... {'cafe': 'tea', 'tea': 'cafe', 'like': 'prefer'})
'Do you prefer tea? No, I prefer cafe.'
Lo principal para mí es que también puedes usar expresiones regulares, por ejemplo para reemplazar solo palabras enteras o para normalizar espacios en blanco:
>>> s = "I don't want to change this name:\n Philip II of Spain"
>>> re_str_dict = {r'\bI\b': 'You', r'[\n\t ]+': ' '}
>>> multiple_replace(s, re_str_dict)
"You don't want to change this name: Philip II of Spain"
Si desea utilizar las teclas del diccionario como cadenas normales, puede escapar de ellas antes de llamar a multiple_replace utilizando, por ejemplo, esta función:
def escape_keys(d):
""" transform dictionary d by applying re.escape to the keys """
return dict((re.escape(k), v) for k, v in d.items())
>>> multiple_replace(s, escape_keys(re_str_dict))
"I don't want to change this name:\n Philip II of Spain"
La siguiente función puede ayudar a encontrar expresiones regulares erróneas entre las teclas del diccionario (ya que el mensaje de error de multiple_replace no es muy revelador):
def check_re_list(re_list):
""" Checks if each regular expression in list is well-formed. """
for i, e in enumerate(re_list):
try:
re.compile(e)
except (TypeError, re.error):
print("Invalid regular expression string "
"at position {}: '{}'".format(i, e))
>>> check_re_list(re_str_dict.keys())
Tenga en cuenta que no encadena los reemplazos, sino que los realiza simultáneamente. Esto lo hace más eficiente sin limitar lo que puede hacer. Para imitar el efecto del encadenamiento, es posible que solo necesite agregar más pares de reemplazo de cadenas y garantizar el orden esperado de los pares:
>>> multiple_replace("button", {"but": "mut", "mutton": "lamb"})
'mutton'
>>> multiple_replace("button", [("button", "lamb"),
... ("but", "mut"), ("mutton", "lamb")])
'lamb'