Por que 3 barras invertidas equivalem a 4 em uma string Python?

90

Você poderia me dizer por que '?\\\?'=='?\\\\?'True? Isso me deixa louco e não consigo encontrar uma resposta razoável ...

>>> list('?\\\?')
['?', '\\', '\\', '?']
>>> list('?\\\\?')
['?', '\\', '\\', '?']
kozooh
fonte
8
Este último não está escapando de nada, então acaba escapando por si mesmo
Padraic Cunningham
1
Não há necessidade de incluir list()mesmo:>>> '?\\\?' '?\\\\?'
daboross 01 de
@PadraicCunningham Não "acaba escapando de si mesmo". Afinal, o que isso quer dizer?
user253751 01 de
Curiosamente, o motivo é que ambos são iguais a duas barras invertidas :-)
RemcoGerlich
@immibis, é exatamente isso que está acontecendo. Você sabe a diferença entre repr e str? Tente imprimir ambos com uma barra invertida na string e pode ficar claro
Padraic Cunningham

Respostas:

84

Basicamente, porque o python é ligeiramente tolerante no processamento de barra invertida. Citando https://docs.python.org/2.0/ref/strings.html :

Ao contrário do C padrão, todas as sequências de escape não reconhecidas são deixadas na string inalteradas, ou seja, a barra invertida é deixada na string .

(Ênfase no original)

Portanto, em python, não é que três barras invertidas são iguais a quatro, é que quando você segue a barra invertida com um caractere como ?, os dois juntos surgem como dois caracteres, porque \?não é uma sequência de escape reconhecida.

Daniel Martin
fonte
6
Isso é o oposto de leniente. Lenient é o comportamento de quase todos os outros de "se você aplicar uma barra invertida em um personagem que não precisa, a barra invertida não fará nada". Junto com outra convenção (que os alfanuméricos de barra invertida podem torná-los especiais, mas a pontuação de barra invertida sempre a torna não especial), você obtém a propriedade muito boa de que pode desfazer com segurança uma string colocando barra invertida em toda a pontuação, sem ter que saber quais caracteres são especialmente interpetado - uma propriedade que falta ao Python.
hobbs de
24
Não, o oposto de tolerante seria gerar um erro ao usar um escape de barra invertida não reconhecido. (Como quase todas as linguagens compiladas fazem. Lembre-se de que o processamento de strings do Python é basicamente "como C, exceto que não explodimos quando recebem escapes de barra invertida inválidos") Além disso, em uma string, qualquer que seja a linguagem, há apenas dois caracteres que precisam de escape - tudo o que você está usando como delimitador e a própria barra invertida. Não entendo o argumento de que é difícil lembrar os dois.
Daniel Martin
@DanielMartin existem algumas linguagens onde o delimitador funciona como seu próprio caractere de escape (por exemplo 'escape''d'). Você nem mesmo precisa se lembrar de outros personagens lá!
SztupY 01 de
1
Oh, espere, acho que o pascal padrão também usou esse sistema - veja nyx.net/~gthompso/self_pasc.txt
Daniel Martin
1
@DanielMartin SQL também.
Random832
30

Isso ocorre porque a barra invertida atua como um caractere de escape para o (s) caractere (s) imediatamente seguinte (s), se a combinação representar uma sequência de escape válida. A dúzia ou mais de sequências de escape estão listadas aqui . Eles incluem os óbvios, como nova linha \n, tabulação horizontal \t, retorno de carro \re outros mais obscuros, como caracteres Unicode nomeados usando \N{...}, por exemplo, \N{WAVY DASH}que representa caractere Unicode \u3030. O ponto principal é que, se a sequência de escape não for conhecida, a sequência de caracteres será deixada na string como está.

Parte do problema também pode ser que a saída do interpretador Python está enganando você. Isso ocorre porque as barras invertidas têm escape quando exibidas. No entanto, se você imprimir essas strings, verá as barras invertidas extras desaparecerem.

>>> '?\\\?'
'?\\\\?'
>>> 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 seus exemplos específicos, no primeiro caso '?\\\?', o primeiro \escapa da segunda barra invertida deixando uma única barra invertida, mas a terceira barra invertida permanece como uma barra invertida porque \?não é uma sequência de escape válida. Portanto, a string resultante é ?\\?.

Para o segundo caso '?\\\\?', a primeira barra invertida escapa da segunda e a terceira barra invertida escapa da quarta, que resulta na string ?\\?.

É por isso que três barras invertidas são iguais a quatro:

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

Se você deseja criar uma string com 3 barras invertidas, pode escapar de cada barra invertida:

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

ou você pode achar strings "brutas" mais compreensíveis:

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

Isso muda o processamento da sequência de escape para o literal de string. Consulte Literais de string para obter mais detalhes.

mhawke
fonte
Você está certo '?\\\?'=='?\\?'False, eu digitei errado. Isso deve ser '?\\\?'=='?\\\\?'como a pergunta indica, eu corrigi.
kozooh
13

Porque \xem uma string de caracteres, quando xnão é um dos caracteres especiais backslashable como n,r , t, 0, etc, avaliada como uma string com uma barra invertida e, em seguida, um x.

>>> '\?'
'\\?'
o paulo
fonte
7

Na página de análise lexical python em literais de string em: https://docs.python.org/2/reference/lexical_analysis.html

Existe uma tabela que lista todas as sequências de escape reconhecidas.

\\ é uma sequência de escape que é === \

\? não é uma sequência de escape e é === \?

então '\\\\' é '\\' seguido por '\\' que é '\\' (dois com escape \)

e '\\\' é '\\' seguido por '\' que também é '\\' (um com escape \ e um bruto \)

Além disso, deve-se observar que o python não distingue entre aspas simples e duplas em torno de um literal de string, ao contrário de algumas outras linguagens.

Portanto, 'String' e "String" são exatamente a mesma coisa em python, eles não afetam a interpretação das sequências de escape.

rkh
fonte
1

A resposta de mhawke praticamente cobre isso, eu só quero reformulá-la de uma forma mais concisa e com exemplos mínimos que ilustram esse comportamento.

Acho que uma coisa a acrescentar é que o processamento de escape se move da esquerda para a direita, de modo que \nprimeiro encontra a barra invertida e, em seguida, procura um caractere para escapar, em seguida, encontra ne escapa; \\nencontra a primeira barra invertida, encontra a segunda e escapa dela, então a encontra ne a vê como um n literal; \?encontra a barra invertida e procura um caractere para escapar, encontra o ?que não pode ser escapado, e então trata\ como uma barra invertida literal.

Como mhawke observou, a chave aqui é que o interpretador interativo escapa da barra invertida ao exibir uma string. Suponho que a razão para isso é garantir que as strings de texto copiadas do interpretador para o editor de código sejam strings de python válidas. No entanto, neste caso, essa permissão para conveniência causa confusão.

>>> 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
\\?
Chuvoso
fonte