Header Ads Widget

Ticker

6/recent/ticker-posts

Cómo comparar cadenas en Python: igualdad e identidad

 Una vez más, volvemos con otro tema de Python. Hoy hablaremos sobre cómo comparar cadenas en Python. Normalmente, trato de mantenerme alejado de las cadenas porque tienen mucha complejidad (por ejemplo, diferentes lenguajes, implementaciones, etc.). Dicho esto, decidí arriesgarme con este. ¡Espero que te guste!

Como un pequeño adelanto, esto es lo que puede esperar en este artículo. Vamos a estar buscando en algunos diversos operadores de comparación en Python, incluyendo ==<<=>=, y >así como isAdemás, hablaremos sobre cómo se pueden usar estos operadores para comparar cadenas y cuándo usarlos. Si quieres saber más, tendrás que seguir leyendo.

Descripción del problema

Imaginemos que estamos construyendo un motor de búsqueda simple. Por ejemplo, tenemos un montón de archivos con texto en ellos, y queremos poder buscar en esos documentos ciertas palabras clave. ¿Cómo haríamos eso?

En el núcleo de este motor de búsqueda, tendremos que comparar cadenas. Por ejemplo, si buscamos en nuestro sistema algo sobre los pingüinos de Pittsburgh (por ejemplo, Sidney Crosby), tendremos que buscar documentos que contengan nuestra palabra clave. Por supuesto, ¿cómo sabemos si tenemos una coincidencia o no?

Específicamente, queremos saber cómo podemos comparar dos cadenas para la igualdad. Por ejemplo, ¿"Sidney Crosby" es lo mismo que "Sidney Crosby"? ¿Qué tal "Sidney Crosby"? ¿O incluso "SiDnEy CrOsBy"? En otras palabras, ¿qué constituye la igualdad en Python?

Por supuesto, la igualdad no es la única forma de comparar cadenas. Por ejemplo, ¿cómo podemos comparar cadenas alfabéticamente / lexicográficamente? ¿"Malkin" viene antes o después de "Letang" en una lista?

Si alguno de estos temas te parece interesante, estás de suerte. Los cubriremos todos y más en este artículo.

Soluciones

En esta sección, veremos algunas formas diferentes de comparar cadenas. Primero, veremos una solución de fuerza bruta que implica recorrer cada personaje para buscar coincidencias. Luego, presentaremos los operadores de comparación que abstraen la solución de fuerza bruta. Finalmente, hablaremos de identidad.

Comparar cuerdas por fuerza bruta

Dado que las cadenas son iterables, no hay nada que realmente nos impida escribir un bucle para comparar cada carácter:

1
2
3
4
5
6
7
penguins_87 = "Crosby"
penguins_71 = "Malkin"
is_same_player = True
for a, b in zip(penguins_87, penguins_71):
  if a != b:
    is_same_player = False
    break

En este ejemplo, comprimimos ambas cadenas y recorremos cada par de caracteres hasta que no encontramos una coincidencia. Si rompemos antes de terminar, sabemos que no tenemos partido. De lo contrario, nuestras cadenas son "idénticas".

Si bien esto hace el trabajo para algunas cadenas, puede fallar en ciertos escenarios. Por ejemplo, ¿qué sucede si una de las cadenas es más larga que la otra?

1
2
3
penguins_87 = "Crosby"
penguins_71 = "Malkin"
penguins_59 = "Guentzel"

Resulta zip()que en realidad truncará la cadena más larga. Para lidiar con eso, podríamos considerar hacer una verificación de longitud primero:

1
2
3
4
5
6
7
8
9
penguins_87 = "Crosby"
penguins_71 = "Malkin"
penguins_59 = "Guentzel"
is_same_player = len(penguins_87) == len(penguins_59)
if is_same_player:
  for a, b in zip(penguins_87, penguins_59):
    if a != b:
      is_same_player = False
      break

Por supuesto, incluso con la verificación adicional, esta solución es un poco exagerada y probablemente propensa a errores. Además, esta solución solo funciona para la igualdad. ¿Cómo comprobamos si una cadena es "menor" que otra lexicográficamente? Afortunadamente, hay otras soluciones a continuación.

Comparar cadenas por operadores de comparación

Dato curioso: no tenemos que escribir nuestro propio código de igualdad de cadenas para comparar cadenas. Como resultado, hay varios operadores de centrales que actúan con cadenas nada más sacarlo de la caja: ==<<=>=>.

Usando nuestros jugadores de Penguins de arriba, podemos intentar compararlos directamente:

1
2
3
4
5
6
7
8
penguins_87 = "Crosby"
penguins_71 = "Malkin"
penguins_59 = "Guentzel"
 
penguins_87 == penguins_87  # True
penguins_87 == penguins_71  # False
penguins_87 >= penguins_71  # False
penguins_59 <= penguins_71  # True

Ahora, es importante tener en cuenta que estos operadores de comparación funcionan con la representación ASCII subyacente de cada carácter. Como resultado, es posible que las cadenas aparentemente equivalentes no parezcan ser las mismas:

1
2
3
4
penguins_87 = "Crosby"
penguins_87_small = "crosby"
 
penguins_87 == penguins_87_small  # False

Cuando comparamos "Crosby" y "crosby", obtenemos Falseporque "c" y "C" no son equivalentes:

1
2
ord('c')  # 99
ord('C')  # 67

Naturalmente, esto puede provocar un comportamiento extraño. Por ejemplo, podríamos decir que "crosby" es menor que "Malkin" porque "crosby" viene antes de "Malkin" alfabéticamente. Desafortunadamente, no es así como Python interpreta esa expresión:

1
2
3
4
penguins_87_small = "crosby"
penguins_71 = "Malkin"
 
penguins_87_small < penguins_71  # False

En otras palabras, si bien estos operadores de comparación son convenientes, en realidad no realizan una comparación que no distingue entre mayúsculas y minúsculas. Afortunadamente, hay todo tipo de trucos que podemos emplear, como convertir ambas cadenas a mayúsculas o minúsculas:

1
2
3
4
5
penguins_87_small = "crosby"
penguins_71 = "Malkin"
 
penguins_87_small.lower() < penguins_71.lower()
penguins_87_small.upper() < penguins_71.upper()

Dado que las cadenas en Python son inmutables como la mayoría de los lenguajes, estos métodos en realidad no manipulan las cadenas subyacentes. En cambio, los devuelven nuevos.

Dicho todo esto, las cadenas son intrínsecamente complejas. Digo que tiene un poco de advertencia porque es probable que haya casos extremos en los que las soluciones de este artículo no funcionen como se esperaba. Después de todo, solo hemos arañado la superficie con caracteres ASCII. Intente jugar con algunas cadenas que no incluyan caracteres en inglés (por ejemplo, 🤐, 汉, etc.). Puede que te sorprendan los resultados.

Comparar cadenas por identidad

Antes de continuar, sentí que era importante mencionar otra forma de comparar cadenas: identidad. En Python, ==no es la única forma de comparar cosas; también podemos usar isEchar un vistazo:

1
2
3
4
5
6
penguins_87 = "Crosby"
penguins_71 = "Malkin"
penguins_59 = "Guentzel"
 
penguins_87 is penguins_87  # True
penguins_87 is penguins_71  # False

Aquí, es difícil ver algún tipo de diferencia entre esta solución y la anterior. Después de todo, el resultado es el mismo. Dicho esto, aquí hay una diferencia fundamental. Con igualdad ( ==), comparamos las cadenas por su contenido (es decir, letra por letra). Con identity ( is), comparamos las cadenas por su ubicación en la memoria (es decir, dirección / referencia).

Para ver esto en acción, creemos algunas cadenas equivalentes:

01
02
03
04
05
06
07
08
09
10
11
penguins_87 = "Crosby"
penguins_87_copy = "Crosby"
penguins_87_clone = "Cros" + "by"
penguins_8 = "Cros"
penguins_7 = "by"
penguins_87_dupe = penguins_8 + penguins_7
 
id(penguins_87)        # 65564544
id(penguins_87_copy)   # 65564544
id(penguins_87_clone)  # 65564544
id(penguins_87_dupe)   # 65639392 Uh Oh!

En los primeros tres ejemplos, el intérprete de Python pudo decir que las cadenas construidas eran las mismas, por lo que el intérprete no se molestó en hacer espacio para los dos clones. En cambio, les dio a los dos últimos penguins_87_copypenguins_87_clonela misma identificación. Como resultado, si comparamos cualquiera de las tres primeras cadenas con ==is, obtendremos el mismo resultado:

1
2
penguins_87 == penguins_87_copy == penguins_87_clone  # True
penguins_87 is penguins_87_copy is penguins_87_clone  # True

Cuando llegamos a la última cadena penguins_87_dupe, nos encontramos con un pequeño problema. Por lo que puedo decir, el intérprete no puede saber cuál es el valor de la expresión hasta el tiempo de ejecución. Como resultado, crea una nueva ubicación para la cadena resultante, a pesar de que "Crosby" ya existe. Si modificamos nuestras cadenas de comparación desde arriba, veremos un resultado diferente:

1
2
penguins_87 == penguins_87_copy == penguins_87_clone == penguins_87_dupe # True
penguins_87 is penguins_87_copy is penguins_87_clone is penguins_87_dupe # False

La conclusión principal aquí es usar solo ==cuando se comparan cadenas para la igualdad (cualquier objeto para el caso). Después de todo, no hay garantía de que el intérprete de Python identifique correctamente cadenas equivalentes y les dé la misma ID. Dicho esto, si necesita comparar dos cadenas de identidad, este es el camino a seguir.

Desafío

Normalmente, verificaría el rendimiento de cada solución, pero no son tan similares. En cambio, pensé que podríamos saltar directamente al desafío.

Ahora que sabemos cómo comparar cadenas en Python, pensé que podríamos intentar usar ese conocimiento para escribir un algoritmo simple de clasificación de cadenas. Para este desafío, puede asumir cadenas ASCII y distinción entre mayúsculas y minúsculas. Sin embargo, puede optimizar sus soluciones según sea necesario. Todo lo que me importa es el uso de los operadores discutidos en este artículo.

Si necesita una lista de muestra para comenzar, aquí está la lista actual de los Pingüinos de Pittsburgh (ordenados alfabéticamente al revés):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
dieciséis
17
18
penguins_2019_2020 = [
  'Tanev',
  'Simon',
  'Rust',
  'McCann',
  'Malkin',
  'Lafferty',
  'Kahun',
  'Hornqvist',
  'Guentzel',
  'Galchenyuk',
  'Di Pauli',
  'Crosby',
  'Blueger',
  'Blandisi',
  'Bjugstad',
  'Aston-Reese'
]

Cuando haya terminado, deje caer su solución en los comentarios a continuación. Luego, diríjase a mi artículo titulado Cómo ordenar una lista de cadenas en Python para ver algunas soluciones inteligentes.

Un pequeño resumen

Y con eso, hemos terminado. Vea todas las soluciones aquí:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
dieciséis
17
18
19
20
penguins_87 = "Crosby"
penguins_71 = "Malkin"
penguins_59 = "Guentzel"
 
# Brute force comparison (equality only)
is_same_player = len(penguins_87) == len(penguins_59)
if is_same_player:
  for a, b in zip(penguins_87, penguins_59):
    if a != b:
      is_same_player = False
      break
 
# Direct comparison
penguins_87 == penguins_59  # False
penguins_87 > penguins_59  # False
penguins_71 <= penguins_71  # True
 
# Identity checking
penguins_87 is penguins_87  # True
penguins_71 is penguins_87  # False

Publicar un comentario

0 Comentarios