Odpowiedź na pytanie jest już wystarczająca (tj. Odpowiedź @ Paula Rooneya ), ale można również zweryfikować poprawność tych odpowiedzi.
Pozwól, że podsumuję istniejące odpowiedzi: To ..
nie jest pojedynczy element składniowy!
Możesz sprawdzić, w jaki sposób kod źródłowy jest „tokenizowany” . Te tokeny przedstawiają sposób interpretacji kodu:
>>> 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__'),
...]
Więc ciąg 1.
interpretowany jest jako liczba, drugim .
jest OP (operator, w tym przypadku operator „get atrybut”), a __truediv__
nazwa metody. To tylko dostęp do __truediv__
metody float 1.0
.
Innym sposobem przeglądania wygenerowanego kodu bajtowego jest jego złożenie . To faktycznie pokazuje instrukcje, które są wykonywane, gdy wykonywany jest jakiś kod: 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
Co w zasadzie mówi to samo. Ładuje atrybut __truediv__
stałej 1.0
.
Jeśli chodzi o twoje pytanie
I jak możesz go użyć w bardziej złożonym zestawieniu (jeśli to możliwe)?
Chociaż jest to możliwe, nigdy nie powinieneś pisać takiego kodu, po prostu dlatego, że nie jest jasne, co robi kod. Dlatego nie używaj go w bardziej złożonych instrukcjach. Posunąłbym się nawet tak daleko, że nie powinieneś używać go w tak „prostych” instrukcjach, przynajmniej powinieneś użyć nawiasu do rozdzielenia instrukcji:
f = (1.).__truediv__
byłoby to zdecydowanie bardziej czytelne - ale coś w stylu:
from functools import partial
from operator import truediv
f = partial(truediv, 1.0)
byłoby jeszcze lepiej!
Podejście wykorzystujące partial
zachowuje również model danych Pythona ( 1..__truediv__
podejście nie!), Co można zademonstrować za pomocą tego małego fragmentu:
>>> 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
Wynika to z tego, że 1. / (1+2j)
nie jest analizowane przez, float.__truediv__
ale z complex.__rtruediv__
- operator.truediv
upewnia się, że operacja odwrotna jest wywoływana, gdy powróci normalna operacja, NotImplemented
ale nie wystąpią te awarie, gdy operujesz __truediv__
bezpośrednio. Ta utrata „oczekiwanego zachowania” jest głównym powodem, dla którego (zwykle) nie powinieneś bezpośrednio używać magicznych metod.
(1).__truediv__
tak naprawdę nie jest tym samym1..__truediv__
, co poprzednie wywołania,int.__truediv__
podczas gdy drugiefloat.__truediv__
. Możesz też użyć1 .__truediv__
(ze spacją) `