Che cosa è f = 1..__truediv__
?
f
è un metodo speciale associato su un float con un valore di uno. In particolare,
1.0 / x
in Python 3, invoca:
(1.0).__truediv__(x)
Prova:
class Float(float):
def __truediv__(self, other):
print('__truediv__ called')
return super(Float, self).__truediv__(other)
e:
>>> one = Float(1)
>>> one/2
__truediv__ called
0.5
Se lo facciamo:
f = one.__truediv__
Conserviamo un nome associato a quel metodo associato
>>> f(2)
__truediv__ called
0.5
>>> f(3)
__truediv__ called
0.3333333333333333
Se stessimo eseguendo quella ricerca punteggiata in un ciclo stretto, questo potrebbe far risparmiare un po 'di tempo.
Parsing the Abstract Syntax Tree (AST)
Possiamo vedere che l'analisi del AST per l'espressione ci dice che stiamo ottenendo l' __truediv__
attributo del numero in virgola mobile, 1.0
:
>>> import ast
>>> ast.dump(ast.parse('1..__truediv__').body[0])
"Expr(value=Attribute(value=Num(n=1.0), attr='__truediv__', ctx=Load()))"
È possibile ottenere la stessa funzione risultante da:
f = float(1).__truediv__
O
f = (1.0).__truediv__
Deduzione
Possiamo anche arrivarci per detrazione.
Costruiamolo.
1 da solo è un int
:
>>> 1
1
>>> type(1)
<type 'int'>
1 con un punto dopo che è un float:
>>> 1.
1.0
>>> type(1.)
<type 'float'>
Il punto successivo da solo sarebbe un SyntaxError, ma inizia una ricerca punteggiata sull'istanza del float:
>>> 1..__truediv__
<method-wrapper '__truediv__' of float object at 0x0D1C7BF0>
Nessun altro ha parlato di questo - Questo è ormai un "metodo vincolato" sul galleggiante, 1.0
:
>>> f = 1..__truediv__
>>> f
<method-wrapper '__truediv__' of float object at 0x127F3CD8>
>>> f(2)
0.5
>>> f(3)
0.33333333333333331
Potremmo svolgere la stessa funzione in modo molto più leggibile:
>>> def divide_one_by(x):
... return 1.0/x
...
>>> divide_one_by(2)
0.5
>>> divide_one_by(3)
0.33333333333333331
Prestazione
Il rovescio della medaglia della divide_one_by
funzione è che richiede un altro frame stack Python, rendendolo un po 'più lento del metodo associato:
>>> def f_1():
... for x in range(1, 11):
... f(x)
...
>>> def f_2():
... for x in range(1, 11):
... divide_one_by(x)
...
>>> timeit.repeat(f_1)
[2.5495760687176485, 2.5585621018805469, 2.5411816588331888]
>>> timeit.repeat(f_2)
[3.479687248616699, 3.46196088706062, 3.473726342237768]
Certo, se puoi semplicemente usare letterali semplici, è ancora più veloce:
>>> def f_3():
... for x in range(1, 11):
... 1.0/x
...
>>> timeit.repeat(f_3)
[2.1224895628296281, 2.1219930218637728, 2.1280188256941983]
(1).__truediv__
non è proprio lo stesso di1..__truediv__
, come il primo chiamaint.__truediv__
mentre il secondo lo fafloat.__truediv__
. In alternativa, puoi anche usare1 .__truediv__
(con uno spazio) `