Técnica de comparación de cuerdas utilizada por Python

Me pregunto cómo Python hace la comparación de cadenas, más específicamente cómo determina el resultado cuando se utiliza un operador menor que ( < ) o mayor que ( > ).

Por ejemplo, si puse print('abc' < 'bac') obtendré True . Entiendo que compara los caracteres correspondientes en la cadena, sin embargo, no está claro por qué hay más, por falta de un término mejor, “peso” puesto en el hecho de que a es menor que b (primera posición) en la primera cadena en lugar de el hecho de que a es menor que b en la segunda cadena (segunda posición).

De los documentos :

La comparación utiliza ordenamiento lexicográfico: primero se comparan los dos primeros elementos, y si difieren esto determina el resultado de la comparación; si son iguales, se comparan los dos elementos siguientes, y así sucesivamente, hasta que se agote cualquiera de las secuencias.

También:

El orden lexicográfico de las cadenas utiliza el número de punto del código Unicode para ordenar caracteres individuales.

o en Python 2 :

El ordenamiento lexicográfico para cadenas usa el ordenamiento ASCII para caracteres individuales.

Como ejemplo:

 >>> 'abc' > 'bac' False >>> ord('a'), ord('b') (97, 98) 

El resultado False se devuelve tan pronto como se encuentre que a es menor que b . Los demás elementos no se comparan (como puede ver para los segundos elementos: b > a es True ).

Esté atento a las mayúsculas y minúsculas:

 >>> [(x, ord(x)) for x in abc] [('a', 97), ('b', 98), ('c', 99), ('d', 100), ('e', 101), ('f', 102), ('g', 103), ('h', 104), ('i', 105), ('j', 106), ('k', 107), ('l', 108), ('m', 109), ('n', 110), ('o', 111), ('p', 112), ('q', 113), ('r', 114), ('s', 115), ('t', 116), ('u', 117), ('v', 118), ('w', 119), ('x', 120), ('y', 121), ('z', 122)] >>> [(x, ord(x)) for x in abc.upper()] [('A', 65), ('B', 66), ('C', 67), ('D', 68), ('E', 69), ('F', 70), ('G', 71), ('H', 72), ('I', 73), ('J', 74), ('K', 75), ('L', 76), ('M', 77), ('N', 78), ('O', 79), ('P', 80), ('Q', 81), ('R', 82), ('S', 83), ('T', 84), ('U', 85), ('V', 86), ('W', 87), ('X', 88), ('Y', 89), ('Z', 90)] 

La comparación de cuerdas de Python es lexicográfica:

De Python Docs: http://docs.python.org/reference/expressions.html

Las cadenas se comparan de forma lexicográfica utilizando los equivalentes numéricos (el resultado de la función incorporada ord ()) de sus caracteres. Unicode y las cadenas de 8 bits son totalmente interoperables en este comportamiento.

Por lo tanto, en su ejemplo, 'abc' < 'bac' , 'a' aparece antes (menos que) 'b' numéricamente (en ASCII y representaciones de Unicode), por lo que la comparación termina ahí.

Python y casi todos los demás lenguajes informáticos utilizan los mismos principios que (espero) utilizarías al encontrar una palabra en un diccionario impreso:

(1) Dependiendo del lenguaje humano involucrado, tiene una noción de orden de caracteres: ‘a’ <'b' <'c' etc.

(2) El primer carácter tiene más peso que el segundo: ‘az’ <'za' (si el idioma está escrito de izquierda a derecha o de derecha a izquierda o boustrophedon es bastante irrelevante)

(3) Si te quedas sin caracteres para probar, la cadena más corta es menor que la cadena más larga: ‘foo’ <'food'

Normalmente, en un lenguaje informático, la “noción de ordenación de caracteres” es bastante primitiva: cada personaje tiene un número ord(character) independiente del lenguaje humano y los caracteres se comparan y se ordenan utilizando ese número. A menudo, ese ordenamiento no es apropiado para el lenguaje humano del usuario, y luego necesitas entrar en “compaginación”, un tema divertido.

Este es un ordenamiento lexicográfico . Simplemente pone las cosas en orden de diccionario.

Eche un vistazo también a ¿Cómo ordeno las cadenas de Unicode alfabéticamente en Python? donde la discusión es acerca de las reglas de clasificación dadas por el Algoritmo de clasificación de Unicode ( http://www.unicode.org/reports/tr10/ ).

Para responder al comentario.

¿Qué? ¿De qué otra manera se puede definir el pedido de otra manera que no sea de izquierda a derecha?

por S.Lott, hay un famoso contra-ejemplo al clasificar el idioma francés. Implica acentos: de hecho, se podría decir que, en francés, las letras se ordenan de izquierda a derecha y los acentos de derecha a izquierda. Aquí está el contraejemplo: tenemos e <é y o <ô, por lo que cabría esperar que las palabras cote, coté, côte, côté se ordenen como cote

Y un último comentario: no se debe hablar de clasificación de izquierda a derecha y de derecha a izquierda, sino de clasificación hacia adelante y hacia atrás .

De hecho, hay idiomas escritos de derecha a izquierda y, si crees que el árabe y el hebreo están ordenados de derecha a izquierda, puedes tener razón desde un punto de vista gráfico, ¡pero estás equivocado en el nivel lógico!

De hecho, Unicode considera cadenas de caracteres codificadas en orden lógico , y la dirección de escritura es un fenómeno que ocurre en el nivel de glifo. En otras palabras, incluso si en la palabra שלום la letra shin aparece a la derecha del encuestado, lógicamente ocurre antes . Para ordenar esta palabra, primero se considerará la espinilla, luego lamed, luego la vav, luego la mem, y esto es ordenamiento hacia adelante (aunque el hebreo se escribe de derecha a izquierda), mientras que los acentos franceses se ordenan al revés (aunque el francés es escrito de izquierda a derecha).

Un equivalente de Python puro para las comparaciones de cadenas sería:

 def less(string1, string2): # Compare character by character for idx in range(min(len(string1), len(string2))): # Get the "value" of the character ordinal1, ordinal2 = ord(string1[idx]), ord(string2[idx]) # If the "value" is identical check the next characters if ordinal1 == ordinal2: continue # If it's smaller we're finished and can return True elif ordinal1 < ordinal2: return True # If it's bigger we're finished and return False else: return False # We're out of characters and all were equal, so the result depends on the length # of the strings. return len(string1) < len(string2) 

Esta función hace el equivalente del método real ( Python 3.6 y Python 2.7 ) solo un poco más lento. También tenga en cuenta que la implementación no es exactamente "pythonic" y solo funciona para < comparaciones. Es sólo para ilustrar cómo funciona. No he comprobado si funciona como la comparación de Pythons para los caracteres Unicode combinados .

Una variante más general sería:

 from operator import lt, gt def compare(string1, string2, less=True): op = lt if less else gt for char1, char2 in zip(string1, string2): ordinal1, ordinal2 = ord(char1), ord(char1) if ordinal1 == ordinal2: continue elif op(ordinal1, ordinal2): return True else: return False return op(len(string1), len(string2)) 

Las cadenas se comparan de forma lexicográfica utilizando los equivalentes numéricos (el resultado de la función incorporada ord ()) de sus caracteres. Unicode y las cadenas de 8 bits son totalmente interoperables en este comportamiento.

Aquí hay un código de ejemplo que compara dos cadenas lexicográficamente.

  a = str(input()) b = str(input()) if 1<=len(a)<=100 and 1<=len(b)<=100: a = a.lower() b = b.lower() if a > b: print('1') elif a < b: print( '-1') elif a == b: print('0') 

Para diferentes entradas, las salidas son:

 1- abcdefg abcdeff 1 2- abc Abc 0 3- abs AbZ -1