Curso
Es habitual recurrir a .find() o .index() para comprobar si un texto existe en una cadena y encontrarse con comportamientos raros como un ValueError o pasar por alto una coincidencia al inicio de la cadena. La sensibilidad a mayúsculas y minúsculas, los límites de corte desfasados y el hecho de que str no tenga un método .contains() aumentan la confusión. La solución es sencilla: usa la herramienta adecuada. Para comprobar existencia, nos apoyamos en el operador in; para posiciones, en .find()/.index(); para recuentos, en .count(); y para sustituciones, en .replace(), además de algunas técnicas seguras con Unicode cuando importa la insensibilidad a mayúsculas.
Cómo gestiona Python la búsqueda de subcadenas
Las cadenas de texto en Python son Unicode. Las operaciones con subcadenas distinguen mayúsculas por defecto y están implementadas en C para ser rápidas. La forma más directa de comprobar si un texto aparece dentro de una cadena es el operador in, que devuelve un booleano usando la implementación __contains__() de la cadena. Cuando necesites posiciones o recuentos, usa los métodos específicos de str. Los parámetros opcionales start y end siguen la semántica de los cortes: el inicio es inclusivo y el final, exclusivo.
Contención con in
Usa "needle" in haystack cuando solo te importe si la subcadena existe. Es legible y evita manejar índices.
text = "Banana bread"
print("ban" in text) # False (case-sensitive)
print("Ban" in text) # True
Para comprobaciones sin distinguir mayúsculas, normaliza ambos lados. .casefold() es más consciente de Unicode que .lower() y es mi opción por defecto para comparaciones sin mayúsculas.
pattern = "ban"
print(pattern.casefold() in text.casefold()) # True
- Cuándo usarlo: comprobaciones rápidas de existencia, validación de entradas, filtrado de listas.
- Caso límite:
"" in ssiempre esTrue(la cadena vacía está en todas las cadenas).
Método str.find()
Sintaxis
str.find() devuelve el índice más bajo de la subcadena o -1 si no se encuentra. Los parámetros opcionales start y end acotan la búsqueda.
s.find(substring, start=0, end=len(s))
Ejemplos y casos de uso
Usa .find() cuando necesites la posición y puedas gestionar un "no encontrado" sin excepciones.
tagline = "Ship fast. Fix faster."
print(tagline.find("fast")) # 5
print(tagline.find("fast", 6)) # 16
print(tagline.find("slow")) # -1
No escribas if s.find(sub): para comprobar existencia. Si la coincidencia está en el índice 0, la condición es falsy.
name = "Alice"
if name.find("A"): # Mal: 0 es falsy
print("Found A") # No se ejecutará
if name.find("A") != -1: # Correcto
print("Found A")
Método str.index()
Sintaxis
.index() refleja a .find() pero lanza ValueError cuando la subcadena no existe.
s.index(substring, start=0, end=len(s))
Ejemplos y cuándo elegirlo
Usa .index() cuando la subcadena debe existir y una excepción sea la forma adecuada de fallar.
product = "USB-C Cable"
print(product.index("Cable")) # 6
try:
product.index("HDMI")
except ValueError:
print("Required label missing")
Método str.count()
Sintaxis
.count() devuelve el número de apariciones no solapadas. También acepta límites start y end.
s.count(substring, start=0, end=len(s))
Ejemplos y casos límite
Usa .count() para recuentos rápidos sin regex.
log_line = "ERROR: timeout. ERROR: retry. ERROR: gave up."
print(log_line.count("ERROR")) # 3
print(log_line.count("WARN")) # 0
Caso especial: la cadena vacía.
print("abc".count("")) # 4 (len(s) + 1)
Método str.replace()
.replace() devuelve una cadena nueva donde las apariciones de old se sustituyen por new. Usa count para limitar sustituciones.
s.replace(old, new, count=-1)
Sustituir subcadenas
El método no es in-place; devuelve una copia.
message = "The red house is between the blue house and the old house"
print(message.replace("house", "car"))
# The red car is between the blue car and the old car
print(message.replace("house", "car", 2))
# The red car is between the blue car and the old house
Sustitución sin distinguir mayúsculas
Para sustituciones sin distinguir mayúsculas conservando los patrones de capitalización, usa re.sub() con re.IGNORECASE, o normaliza con .casefold() si no necesitas preservar el formato original.
Coincidencias sin distinguir mayúsculas con .casefold()
.casefold() ofrece una normalización agresiva y consciente de Unicode y se prefiere frente a .lower() para comprobaciones sin mayúsculas.
query = "straße" # German 'straße'
text = "STRASSE und mehr" # Uppercase variant
print(query.casefold() in text.casefold()) # True
Herramientas relacionadas para prefijos y sufijos
Si conoces el inicio o el final, usa métodos específicos: son más rápidos y claros que cortar o usar regex:
s.startswith(prefix)ys.endswith(suffix)(aceptan tuplas para múltiples opciones)
s.removeprefix(prefix)ys.removesuffix(suffix)(Python 3.9+)
filename = "draft_report_v2.pdf"
print(filename.removeprefix("draft_")) # report_v2.pdf
print(filename.endswith((".pdf", ".docx"))) # True
Trabajar con columnas de pandas
Al buscar en muchas cadenas de una columna de DataFrame, usa los métodos vectorizados de cadenas. Establece regex=False para hacer coincidencias literales.
import pandas as pd
cities = pd.Series(["New York", "Yorktown", None, "Toronto"], dtype="string")
mask = cities.str.contains("York", regex=False, na=False)
print(mask.tolist()) # [True, True, False, False]
Notas:
- Por defecto,
regex=True, así que caracteres especiales como.coinciden con cualquier carácter si no desactivas regex.
- Gestiona nulos con
na=.... ConStringDtype, los valores ausentes se convierten por defecto enFalseen la máscara.
Errores comunes y casos límite
-
No existe .contains() en str: usa
"sub" in s."abc".contains("a")lanzaAttributeError.
-
Veracidad de .find(): el índice
0es falsy; compara con!= -1o mejor usainpara existencia.
-
.index() lanza excepciones: gestiona
ValueErrorsi la subcadena podría no estar.
-
Cadena vacía:
"" in sesTrue;s.find("")devuelve0;s.count("")eslen(s)+1.
-
Sensibilidad a mayúsculas: todas las operaciones distinguen mayúsculas salvo que normalices o uses flags de regex.
-
Bytes vs. texto: no mezcles
bytesystren búsquedas; decodifica o codifica para igualar tipos.
-
Semántica de subcadenas:
"ab" in "cat,bat"esFalseaunque"a"y"b"aparezcan por separado: las coincidencias deben ser contiguas.
Ejemplos prácticos para ejecutar
Estos fragmentos cortos muestran patrones correctos y señalan tropiezos habituales.
Comprobaciones de existencia
headline = "Where's Waldo?"
print("Waldo" in headline) # True
print("Wenda" in headline) # False
print(headline.find("Waldo")) # 8
print(headline.find("Wenda")) # -1
Búsqueda acotada con start y end
El final es exclusivo, por lo que las posiciones de búsqueda reflejan la semántica de los cortes.
headline = "Where's Waldo?"
print(headline.find("Waldo", 0, 6)) # -1 (buscando en "Where'")
print(headline.find("Waldo", 0, 13)) # 8 (buscando hasta la 'o' de Waldo)
Contar y sustituir
inventory = "apple, apple, pear, apple"
print(inventory.count("apple")) # 3
print(inventory.replace("apple", "orange", 2)) # orange, orange, pear, apple
Ejemplo de práctica de búsqueda y sustitución de cadenas en Python
Este ejemplo procesa una lista de reseñas de películas. Demuestra comprobaciones de existencia, búsquedas acotadas y sustituciones seguras sin regex.
blurbs = [
"Critics say the lead actor delivers a career-best performance.",
"Fans argue the actor actor scene was intentionally repetitive.",
"A clever cameo steals the show."
]
for blurb in blurbs:
# Check whether "actor" appears between indexes 20 and 40 (end exclusive).
window_has_actor = "actor".casefold() in blurb[20:40].casefold()
if not window_has_actor:
print("Window check: actor not found")
continue
# If "actor actor" appears, collapse to a single "actor".
if "actor actor" in blurb:
print(blurb.replace("actor actor", "actor"))
# If "actor actor actor" appears, collapse to a single "actor".
elif "actor actor actor" in blurb:
print(blurb.replace("actor actor actor", "actor"))
else:
print(blurb)
Salida:
Window check: actor not found
Fans argue the actor scene was intentionally repetitive.
Window check: actor not found
Conclusión
Usa el operador in para comprobaciones simples de contención, .find() o .index() cuando necesites posiciones, .count() para recuentos no solapados y .replace() para sustituciones. Normaliza con .casefold() para comprobaciones sin distinguir mayúsculas y, cuando proceda, prefiere los ayudantes específicos de prefijo/sufijo. Evitamos convertir los resultados de .find() a booleanos y tratamos el comportamiento con la subcadena vacía como un caso especial para mantener el código de cadenas correcto y claro.
