Recentemente, deparei com uma sintaxe que nunca vi antes quando aprendi python, nem na maioria dos tutoriais, a ..
notação, é algo parecido com isto:
f = 1..__truediv__ # or 1..__div__ for python 2
print(f(8)) # prints 0.125
Achei que era exatamente o mesmo que (exceto que é mais longo, é claro):
f = lambda x: (1).__truediv__(x)
print(f(8)) # prints 0.125 or 1//8
Mas minhas perguntas são:
- Como isso pode ser feito?
- O que realmente significa com os dois pontos?
- Como você pode usá-lo em uma declaração mais complexa (se possível)?
Provavelmente isso me salvará muitas linhas de código no futuro ... :)
(1).__truediv__
não é realmente o mesmo que1..__truediv__
, como o primeiro chamaint.__truediv__
enquanto o último chamafloat.__truediv__
. Como alternativa, você também pode usar1 .__truediv__
(com um espaço) `1//8
ocorre nas duas versões do Python.0
0.125
if (x <- 3) {...}
Respostas:
O que você tem é um
float
literal sem o zero à direita, ao qual você acessa o__truediv__
método Não é um operador em si; o primeiro ponto faz parte do valor flutuante e o segundo é o operador de ponto para acessar as propriedades e os métodos dos objetos.Você pode chegar ao mesmo ponto, fazendo o seguinte.
Outro exemplo
Aqui nós adicionamos 1.0 a 2.0, o que obviamente gera 3.0.
fonte
1..toString()
A pergunta já está suficientemente respondida (por exemplo, a resposta de @Paul Rooney ), mas também é possível verificar a exatidão dessas respostas.
Deixe-me recapitular as respostas existentes: O
..
elemento não é um único sintaxe!Você pode verificar como o código-fonte é "tokenizado" . Esses tokens representam como o código é interpretado:
Portanto, a string
1.
é interpretada como número, a segunda.
é um OP (um operador, neste caso o operador "get attribute") e__truediv__
é o nome do método. Então, isso é apenas acessar o__truediv__
método do float1.0
.Outra maneira de visualizar o bytecode gerado é montá- lo. Na verdade, isso mostra as instruções executadas quando algum código é executado:
dis
O que basicamente diz o mesmo. Carrega o atributo
__truediv__
da constante1.0
.Quanto à sua pergunta
Mesmo que seja possível, você nunca deve escrever um código como esse, simplesmente porque não está claro o que o código está fazendo. Portanto, não o use em declarações mais complexas. Eu iria tão longe que você não deve usá-lo em declarações "simples", pelo menos você deve usar parênteses para separar as instruções:
isso seria definitivamente mais legível - mas algo como:
seria ainda melhor!
A abordagem usando
partial
também preserva o modelo de dados do python (a1..__truediv__
abordagem não!), O que pode ser demonstrado por este pequeno trecho:Isso ocorre porque
1. / (1+2j)
não é avaliado por,float.__truediv__
mas comcomplex.__rtruediv__
-operator.truediv
garante que a operação reversa seja chamada quando a operação normal retornar,NotImplemented
mas você não terá esses fallbacks quando operar__truediv__
diretamente. Essa perda de "comportamento esperado" é a principal razão pela qual você (normalmente) não deve usar métodos mágicos diretamente.fonte
Dois pontos juntos podem ser um pouco estranhos no começo:
Mas é o mesmo que escrever:
Como os
float
literais podem ser escritos em três formas:fonte
1.__truediv__
não são?.
parece ser analisado como parte do número e, em seguida, o.
acessador do método está ausente.f
é um método especial vinculado em um float com o valor one. Especificamente,no Python 3, chama:
Evidência:
e:
Se nós fizermos:
Mantemos um nome vinculado a esse método vinculado
Se estivéssemos fazendo essa pesquisa pontilhada em um loop apertado, isso poderia economizar um pouco de tempo.
Analisando a Árvore de Sintaxe Abstrata (AST)
Podemos ver que a análise do AST da expressão nos diz que estamos obtendo o
__truediv__
atributo no número do ponto flutuante1.0
:Você pode obter a mesma função resultante de:
Ou
Dedução
Também podemos chegar lá por dedução.
Vamos construir.
1 por si só é um
int
:1 com um período após ser flutuado:
O próximo ponto por si só seria um SyntaxError, mas inicia uma pesquisa pontilhada na instância do float:
Ninguém mais mencionou isso - Agora é um "método vinculado" no float
1.0
:Poderíamos realizar a mesma função com muito mais facilidade:
atuação
A desvantagem da
divide_one_by
função é que ela requer outro quadro de pilha Python, tornando-o um pouco mais lento que o método bound:Obviamente, se você pode usar literais simples, isso é ainda mais rápido:
fonte