¿Por qué 3 barras diagonales inversas equivalen a 4 en una cadena de Python?

90

¿Podrías decirme por qué '?\\\?'=='?\\\\?'da True? Eso me vuelve loco y no encuentro una respuesta razonable ...

>>> list('?\\\?')
['?', '\\', '\\', '?']
>>> list('?\\\\?')
['?', '\\', '\\', '?']
kozooh
fuente
8
Este último no se escapa de nada, por lo que termina escapándose él mismo
Padraic Cunningham
1
No es necesario incluir list()incluso:>>> '?\\\?' '?\\\\?'
daboross
@PadraicCunningham No "acaba escapándose de sí mismo". ¿Y eso que significa?
user253751
Curiosamente, la razón es que ambos son iguales a dos barras invertidas :-)
RemcoGerlich
@immibis, eso es exactamente lo que está sucediendo. ¿Conoces la diferencia entre repr y str? Intente imprimir ambos con una barra invertida en la cadena y podría quedar claro
Padraic Cunningham

Respuestas:

84

Básicamente, porque Python es un poco indulgente en el procesamiento de barra invertida. Citando de https://docs.python.org/2.0/ref/strings.html :

A diferencia del Estándar C, todas las secuencias de escape no reconocidas se dejan en la cadena sin cambios, es decir, la barra invertida se deja en la cadena .

(Énfasis en el original)

Por lo tanto, en Python, no es que tres barras invertidas sean iguales a cuatro, es que cuando sigue una barra invertida con un carácter como ?, los dos juntos aparecen como dos caracteres, porque \?no es una secuencia de escape reconocida.

Daniel Martín
fuente
6
Eso es lo contrario de indulgente. Indulgente es el comportamiento de la mayoría de los demás de "si haces una barra invertida en un personaje que no la necesita, la barra invertida no hace nada". Junto con otra convención (que los caracteres alfanuméricos con barra invertida pueden hacerlos especiales, pero la puntuación con barra invertida siempre la hace poco especial), obtiene la propiedad muy agradable de que puede eliminar las cadenas de forma segura mediante la barra invertida en toda la puntuación, sin tener que saber qué caracteres son especialmente interpretado, una propiedad de la que carece Python.
hobbs
24
No, lo contrario de indulgente sería generar un error cuando utiliza un escape de barra invertida no reconocido. (Como casi todos los lenguajes compilados. Recuerde que el procesamiento de cadenas de Python es básicamente "como C, excepto que no explotamos cuando se nos entrega una barra invertida de escape no válida"). Además, en una cadena, sea cual sea el idioma, solo hay dos caracteres que necesitan escapar. - lo que esté usando como delimitador y la barra invertida. No entiendo el argumento de que es difícil recordar ambos.
Daniel Martin
@DanielMartin hay algunos lenguajes donde el delimitador funciona como su propio carácter de escape (por ejemplo 'escape''d'). ¡Ni siquiera tienes que recordar a otros personajes allí!
SztupY
1
Oh, espera, supongo que el pascal estándar también usó ese sistema - ver nyx.net/~gthompso/self_pasc.txt
Daniel Martin
1
@DanielMartin SQL también.
Random832
30

Esto se debe a que la barra invertida actúa como un carácter de escape para los caracteres que le siguen inmediatamente, si la combinación representa una secuencia de escape válida. La docena de secuencias de escape se enumeran aquí . Incluyen los obvios como nueva línea \n, tabulación horizontal \t, retorno de carro \ry otros más oscuros como caracteres Unicode con nombre que utilizan \N{...}, por ejemplo, \N{WAVY DASH}que representa un carácter Unicode \u3030. Sin embargo, el punto clave es que si no se conoce la secuencia de escape, la secuencia de caracteres se deja en la cadena como está.

Parte del problema también podría ser que la salida del intérprete de Python lo engañe. Esto se debe a que las barras invertidas se eliminan cuando se muestran. Sin embargo, si imprime esas cadenas, verá desaparecer las barras invertidas adicionales.

>>> '?\\\?'
'?\\\\?'
>>> print('?\\\?')
?\\?
>>> '?\\\?' == '?\\?'    # I don't know why you think this is True???
False
>>> '?\\\?' == r'?\\?'   # but if you use a raw string for '?\\?'
True
>>> '?\\\\?' == '?\\\?'  # this is the same string... see below
True

Para sus ejemplos específicos, en el primer caso '?\\\?', el primero \escapa de la segunda barra invertida dejando una sola barra invertida, pero la tercera barra invertida permanece como una barra invertida porque \?no es una secuencia de escape válida. Por lo tanto, la cadena resultante es ?\\?.

Para el segundo caso '?\\\\?', la primera barra invertida escapa al segundo y la tercera barra invertida escapa al cuarto, lo que da como resultado la cadena ?\\?.

Por eso, tres barras diagonales inversas son lo mismo que cuatro:

>>> '?\\\?' == '?\\\\?'
True

Si desea crear una cadena con 3 barras invertidas, puede escapar de cada barra invertida:

>>> '?\\\\\\?'
'?\\\\\\?'
>>> print('?\\\\\\?')
?\\\?

o puede encontrar cadenas "crudas" más comprensibles:

>>> r'?\\\?'
'?\\\\\\?'
>>> print(r'?\\\?')
?\\\?

Esto convierte el procesamiento de la secuencia de escape para el literal de cadena. Consulte String Literals para obtener más detalles.

mhawke
fuente
Tienes razón '?\\\?'=='?\\?'da False, lo escribí mal. Eso debería ser '?\\\?'=='?\\\\?'como indica la pregunta, lo he corregido.
kozooh
13

Debido a que \xen una cadena de caracteres, cuando xno es uno de los caracteres especiales como backslashable n, r, t, 0, etc, se evalúa como una cadena con una barra invertida y luego una x.

>>> '\?'
'\\?'
el paul
fuente
7

Desde la página de análisis léxico de Python en literales de cadena en: https://docs.python.org/2/reference/lexical_analysis.html

Hay una tabla que enumera todas las secuencias de escape reconocidas.

\\ es una secuencia de escape que es === \

\? no es una secuencia de escape y es === \?

entonces '\\\\' es '\\' seguido de '\\' que es '\\' (dos escapados \)

y '\\\' es '\\' seguido de '\' que también es '\\' (uno escapado \ y uno sin procesar \)

Además, debe tenerse en cuenta que Python no distingue entre comillas simples y dobles que rodean un literal de cadena, a diferencia de otros lenguajes.

Entonces, 'Cadena' y "Cadena" son exactamente lo mismo en Python, no afectan la interpretación de las secuencias de escape.

rkh
fuente
1

La respuesta de mhawke lo cubre bastante, solo quiero reformularlo de una forma más concisa y con ejemplos mínimos que ilustren este comportamiento.

Supongo que una cosa para agregar es que el procesamiento de escape se mueve de izquierda a derecha, de modo que \nprimero encuentra la barra invertida y luego busca un personaje para escapar, luego lo encuentra ny lo escapa; \\nencuentra la primera barra invertida, busca la segunda y la escapa, luego la encuentra ny la ve como una n literal; \?encuentra barra invertida y busca un carácter para escapar, encuentra ?que no se puede escapar y, por lo tanto, trata \como una barra invertida literal.

Como señaló mhawke, la clave aquí es que el intérprete interactivo escapa de la barra invertida cuando muestra una cadena. Supongo que la razón de esto es asegurarse de que las cadenas de texto copiadas del intérprete al editor de código sean cadenas de Python válidas. Sin embargo, en este caso, esta concesión por conveniencia genera confusión.

>>> print('\?') # \? is not a valid escape code so backslash is left as-is
\?
>>> print('\\?') # \\ is a valid escape code, resulting in a single backslash
'\?'

>>> '\?' # same as first example except that interactive interpreter escapes the backslash
\\?
>>> '\\?' # same as second example, backslash is again escaped
\\?
Lluvioso
fuente