Eliminar caracteres específicos de una cadena en Python

Estoy tratando de eliminar caracteres específicos de una cadena usando Python. Este es el código que estoy usando ahora. Por desgracia, parece que no hace nada a la cadena.

for char in line:
    if char in " ?.!/;:":
        line.replace(char,'')

¿Cómo puedo hacer esto correctamente?

Solución

Las cadenas en Python son inmutables (no pueden ser cambiadas). Debido a esto, el efecto de line.replace(...) es sólo crear una nueva cadena, en lugar de cambiar la anterior. Necesitas rebind (asignar) a linea para que esa variable tome el nuevo valor, con esos caracteres eliminados.

Además, la forma en que lo estás haciendo va a ser un poco lenta, relativamente. También es probable que sea un poco confuso para los pythoneros experimentados, que verán una estructura doblemente anidada y pensarán por un momento que está pasando algo más complicado.

A partir de Python 2.6 y las versiones más recientes de Python 2.x *, puedes utilizar en su lugar str.translate, (pero sigue leyendo para conocer las diferencias con Python 3):

line = line.translate(None, '!@#$')

o el reemplazo de expresiones regulares con re.sub

import re
line = re.sub('[!@#$]', '', line)

Los caracteres encerrados entre paréntesis constituyen una clase de caracteres. Cualquier carácter de linea que esté en esa clase se sustituye por el segundo parámetro de sub: una cadena vacía.

En Python 3, las cadenas son Unicode. kevpie menciona esto en un comentario en una de las respuestas, y está anotado en la documentación de str.translate.

Cuando se llama al método translate de una cadena Unicode, no se puede pasar el segundo parámetro que usamos arriba. Tampoco puedes pasar None como primer parámetro, o incluso una tabla de traducción de string.maketrans. En su lugar, se pasa un diccionario como único parámetro. Este diccionario mapea los valores ordinales de los caracteres (es decir, el resultado de llamar a ord sobre ellos) a los valores ordinales de los caracteres que deberían reemplazarlos, o-usualmente para nosotros-None para indicar que deberían ser eliminados.

Así que para hacer el baile anterior con una cadena Unicode se llamaría algo como

translation_table = dict.fromkeys(map(ord, '!@#$'), None)
unicode_line = unicode_line.translate(translation_table)

Aquí se utiliza dict.fromkeys y map para generar sucintamente un diccionario que contenga

{ord('!'): None, ord('@'): None, ...}

Aún más sencillo, como dice otra respuesta, crear el diccionario en el lugar:

unicode_line = unicode_line.translate({ord(c): None for c in '!@#$'})

* Para la compatibilidad con Python anteriores, puede crear una tabla de traducción "null" para pasarla en lugar de "None":

import string
line = line.translate(string.maketrans('', ''), '!@#$')

Aquí string.maketrans se utiliza para crear una tabla de traducción, que es sólo una cadena que contiene los caracteres con valores ordinales 0 a 255.

Comentarios (7)
line = line.translate(None, " ?.!/;:")
Comentarios (2)

Las cadenas son inmutables en Python. El método replace devuelve una nueva cadena después del reemplazo. Inténtalo:

for char in line:
    if char in " ?.!/;:":
        line = line.replace(char,'')
Comentarios (3)