Por que atribuir a uma lista vazia (por exemplo, [] = “”) não é um erro?

110

No python 3.4, estou digitando

[] = "" 

e funciona bem, nenhuma exceção é levantada. Embora, claro, []não seja igual a ""depois.

[] = ()

também funciona bem.

"" = []

levanta uma exceção conforme o esperado,

() = ""

levanta uma exceção conforme o esperado. Então o que está acontecendo?

Vardd
fonte

Respostas:

132

Você não está comparando por igualdade. Você está atribuindo .

Python permite que você atribua a vários destinos:

foo, bar = 1, 2

atribui os dois valores a fooe bar, respectivamente. Tudo o que você precisa é de uma sequência ou iterável no lado direito e uma lista ou tupla de nomes à esquerda.

Quando você faz:

[] = ""

você atribuiu uma sequência vazia (strings vazias ainda são sequências) a uma lista vazia de nomes.

É essencialmente a mesma coisa que fazer:

[foo, bar, baz] = "abc"

onde você termina com foo = "a", bar = "b"e baz = "c", mas com menos caracteres.

Você não pode, entretanto, atribuir a uma string, portanto, ""no lado esquerdo de uma atribuição nunca funciona e é sempre um erro de sintaxe.

Consulte a documentação das declarações de atribuição :

Uma instrução de atribuição avalia a lista de expressões (lembre-se de que pode ser uma única expressão ou uma lista separada por vírgulas, a última produzindo uma tupla) e atribui o único objeto resultante a cada uma das listas de destino, da esquerda para a direita.

e

A atribuição de um objeto a uma lista de destino, opcionalmente entre parênteses ou colchetes , é definida recursivamente como segue.

Ênfase minha .

O fato de o Python não gerar um erro de sintaxe para a lista vazia é, na verdade, um pequeno bug! A gramática oficialmente documentada não permite uma lista de alvos vazia e, para o vazio, ()você obtém um erro. Veja bug 23275 ; é considerado um bug inofensivo:

O ponto de partida é reconhecer que isso existe há muito tempo e é inofensivo.

Veja também Por que é válido atribuir a uma lista vazia, mas não a uma tupla vazia?

Martijn Pieters
fonte
36

Ele segue as regras da seção de declarações de atribuição da documentação,

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)

Se target listfor uma lista de alvos separados por vírgula: O objeto deve ser iterável com o mesmo número de itens que os alvos na lista de alvos, e os itens são atribuídos, da esquerda para a direita, aos alvos correspondentes.

O objeto deve ser uma sequência com o mesmo número de itens que os alvos na lista de alvos, e os itens são atribuídos, da esquerda para a direita, aos alvos correspondentes.

Então, quando você diz

[] = ""

"" é iterável (qualquer string Python válida é iterável) e está sendo descompactada sobre os elementos da lista.

Por exemplo,

>>> [a, b, c] = "123"
>>> a, b, c
('1', '2', '3')

Como você tem uma string vazia e uma lista vazia, não há nada para desempacotar. Então, nenhum erro.

Mas, tente isso

>>> [] = "1"
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: too many values to unpack (expected 0)
>>> [a] = ""
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: need more than 0 values to unpack

Nesse [] = "1"caso, você está tentando desempacotar a string "1"em uma lista vazia de variáveis. Portanto, ele reclama com "muitos valores para desempacotar (esperado 0)".

Da mesma forma, no [a] = ""caso, você tem uma string vazia, então nada para descompactar realmente, mas você está descompactando sobre uma variável, o que, novamente, não é possível. É por isso que ele reclama "precisa de mais de 0 valores para descompactar".

Além disso, como você notou,

>>> [] = ()

também não lança nenhum erro, porque ()é uma tupla vazia.

>>> ()
()
>>> type(())
<class 'tuple'>

e quando ele é descompactado em uma lista vazia, não há nada para desempacotar. Portanto, nenhum erro.


Mas, quando você faz

>>> "" = []
  File "<input>", line 1
SyntaxError: can't assign to literal
>>> "" = ()
  File "<input>", line 1
SyntaxError: can't assign to literal

como diz a mensagem de erro, você está tentando atribuir a um literal de string. O que não é possível. É por isso que você está recebendo os erros. É como dizer

>>> 1 = "one"
  File "<input>", line 1
SyntaxError: can't assign to literal

Internos

Internamente, esta operação de atribuição será traduzida para o UNPACK_SEQUENCEcódigo op,

>>> dis(compile('[] = ""', "string", "exec"))
  1           0 LOAD_CONST               0 ('')
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               1 (None)

Aqui, como a string está vazia, UNPACK_SEQUENCEdesempacota 0vezes. Mas quando você tem algo assim

>>> dis(compile('[a, b, c] = "123"', "string", "exec"))
  1           0 LOAD_CONST               0 ('123')
              3 UNPACK_SEQUENCE          3
              6 STORE_NAME               0 (a)
              9 STORE_NAME               1 (b)
             12 STORE_NAME               2 (c)
             15 LOAD_CONST               1 (None)
             18 RETURN_VALUE

a sequência 123é descompactada na pilha, da direita para a esquerda. Portanto, o topo da pilha seria, 1o próximo seria 2e o último seria 3. Em seguida, ele atribui do topo da pilha às variáveis ​​da expressão do lado esquerdo, uma por uma.


BTW, em Python, é assim que você pode fazer várias atribuições na mesma expressão. Por exemplo,

a, b, c, d, e, f = u, v, w, x, y, z

isso funciona porque os valores do lado direito são usados ​​para construir uma tupla e então ela será desempacotada sobre os valores do lado esquerdo.

>>> dis(compile('a, b, c, d, e, f = u, v, w, x, y, z', "string", "exec"))
  1           0 LOAD_NAME                0 (u)
              3 LOAD_NAME                1 (v)
              6 LOAD_NAME                2 (w)
              9 LOAD_NAME                3 (x)
             12 LOAD_NAME                4 (y)
             15 LOAD_NAME                5 (z)
             18 BUILD_TUPLE              6
             21 UNPACK_SEQUENCE          6
             24 STORE_NAME               6 (a)
             27 STORE_NAME               7 (b)
             30 STORE_NAME               8 (c)
             33 STORE_NAME               9 (d)
             36 STORE_NAME              10 (e)
             39 STORE_NAME              11 (f)
             42 LOAD_CONST               0 (None)
             45 RETURN_VALUE

mas a técnica clássica de troca a, b = b, ausa rotação de elementos no topo da pilha. Se você tiver apenas dois ou três elementos, eles serão tratados com instruções especiais ROT_TWOe em ROT_THREEvez de construir a tupla e desempacotar.

>>> dis(compile('a, b = b, a', "string", "exec"))
  1           0 LOAD_NAME                0 (b)
              3 LOAD_NAME                1 (a)
              6 ROT_TWO
              7 STORE_NAME               1 (a)
             10 STORE_NAME               0 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE
os quatro olhos
fonte
Você também pode usar dis('[] = ""')sem ligar compile().
Andrea Corbellini
Você pode descrever o que acontece se você estiver trocando mais de três variáveis ​​/ elementos, usando o método em seu último exemplo?
nanofarad de
@hexafraction Ele construirá uma nova tupla com todos os elementos do lado direito e então os descompactará sobre as variáveis ​​do lado esquerdo.
thefourtheye