O que esse comportamento estranho do cólon está fazendo?

104

Estou usando o Python 3.6.1 e me deparei com algo muito estranho. Eu tinha um erro de digitação simples de atribuição de dicionário que levei muito tempo para encontrar.

context = {}
context["a"]: 2
print(context)

Resultado

{}

O que o código está context["a"]: 2fazendo? Não levanta um SyntaxErrorquando deveria IMO. A princípio pensei que fosse criar uma fatia. No entanto, a digitação repr(context["a"]: 2)gera a SyntaxError. Também digitei context["a"]: 2no console e o console não imprimiu nada. Achei que talvez Nonetivesse voltado , mas não tenho tanta certeza.

Também pensei que poderia ser uma instrução if de uma única linha, mas essa também não deve ser a sintaxe correta.

Além disso, context["a"]deve levantar a KeyError.

Estou perplexo. O que está acontecendo?

Justengel
fonte
2
Esta questão já foi enganada e está bem claro que isso é confuso para novatos em Python. Eu acho que isso é pior se Python for sua única linguagem, onde dicas de tipo e definição de variável antes da inicialização em geral podem parecer estranhas. Imagino que apontar um erro seja impossível, pois esse comportamento é deliberado e às vezes útil, conforme explicado no PEP 526, e você não quer quebrar a compatibilidade. No entanto, eu me pergunto se os desenvolvedores de Python considerariam adicionar uma mensagem de aviso útil para alguns casos.
Chris_Rands

Respostas:

98

Você escreveu acidentalmente uma anotação de variável sintaticamente correta . Esse recurso foi introduzido no Python 3.6 (consulte PEP 526 ).

Embora uma anotação de variável seja analisada como parte de uma atribuição anotada , a instrução de atribuição é opcional :

annotated_assignment_stmt ::=  augtarget ":" expression ["=" expression]

Assim, em context["a"]: 2

  • context["a"] é o alvo da anotação
  • 2 é a própria anotação
  • context["a"] é deixado não inicializado

O PEP afirma que "o alvo da anotação pode ser qualquer alvo de atribuição único válido, pelo menos sintaticamente (cabe ao verificador de tipo o que fazer com isso)" , o que significa que a chave não precisa existir para ser anotado (portanto, não KeyError). Aqui está um exemplo do PEP original:

d = {}
d['a']: int = 0  # Annotates d['a'] with int.
d['b']: int      # Annotates d['b'] with int.

Normalmente, a expressão de anotação deve ser avaliada como um tipo Python - afinal, o uso principal das anotações é a sugestão de tipo, mas não é aplicada. A anotação pode ser qualquer expressão Python válida , independentemente do tipo ou valor do resultado.

Como você pode ver, neste momento as dicas de tipo são muito permissivas e raramente úteis, a menos que você tenha um verificador de tipo estático como mypy .

vaultah
fonte
12
Isso não deveria exigir um =operador de atribuição, então? A chave não existe. Isso parece errado para mim.
justengel
1
Nesse caso, : é o operador de atribuição. Estamos apenas "atribuindo" uma anotação de tipo sozinha, não uma chave. Duvido que haja alguma razão para permitir isso, apenas um efeito colateral não intencional de adicionar a sintaxe de anotação.
chepner
1
@chepner Parece que este não é um efeito colateral imho. É exatamente para isso que o PEP correspondente foi projetado.
Ma0 de
6
É estranho que permitirá que você anote um alvo que ainda não foi definido. Se minha primeira linha for x: stre imediatamente seguida por type(x), o intérprete levantará a NameError. IMO a sintaxe deve obrigar o objeto é pré-definido, ou é definido no local. Isso apenas introduz confusão.
revisão de
2
@Idlehands Isso vai contra o propósito. Ter x = 'i am a string'antes x: strtorna o último tipo de redundante. Isso não deveria ter sido adicionado. Foi bom como comentário; Eu nunca mostro isso usado de uma forma ou de outra.
Ma0 de