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:
>>> from tokenize import tokenize
>>> from io import BytesIO
>>> s = "1..__truediv__"
>>> list(tokenize(BytesIO(s.encode('utf-8')).readline))
[...
TokenInfo(type=2 (NUMBER), string='1.', start=(1, 0), end=(1, 2), line='1..__truediv__'),
TokenInfo(type=53 (OP), string='.', start=(1, 2), end=(1, 3), line='1..__truediv__'),
TokenInfo(type=1 (NAME), string='__truediv__', start=(1, 3), end=(1, 14), line='1..__truediv__'),
...]
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 float 1.0
.
Outra maneira de visualizar o bytecode gerado é montá- lo. Na verdade, isso mostra as instruções executadas quando algum código é executado: dis
>>> import dis
>>> def f():
... return 1..__truediv__
>>> dis.dis(f)
4 0 LOAD_CONST 1 (1.0)
3 LOAD_ATTR 0 (__truediv__)
6 RETURN_VALUE
O que basicamente diz o mesmo. Carrega o atributo __truediv__
da constante 1.0
.
Quanto à sua pergunta
E como você pode usá-lo em uma declaração mais complexa (se possível)?
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:
f = (1.).__truediv__
isso seria definitivamente mais legível - mas algo como:
from functools import partial
from operator import truediv
f = partial(truediv, 1.0)
seria ainda melhor!
A abordagem usando partial
também preserva o modelo de dados do python (a 1..__truediv__
abordagem não!), O que pode ser demonstrado por este pequeno trecho:
>>> f1 = 1..__truediv__
>>> f2 = partial(truediv, 1.)
>>> f2(1+2j) # reciprocal of complex number - works
(0.2-0.4j)
>>> f2('a') # reciprocal of string should raise an exception
TypeError: unsupported operand type(s) for /: 'float' and 'str'
>>> f1(1+2j) # reciprocal of complex number - works but gives an unexpected result
NotImplemented
>>> f1('a') # reciprocal of string should raise an exception but it doesn't
NotImplemented
Isso ocorre porque 1. / (1+2j)
não é avaliado por, float.__truediv__
mas com complex.__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.
(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) `