[英]What is `1..__truediv__` ? Does Python have a .. (“dot dot”) notation syntax?
我最近遇到了一個我以前從未見過的語法,當我學習python時,也沒有在大多數教程中使用..
符號,它看起來像這樣:
f = 1..__truediv__ # or 1..__div__ for python 2
print(f(8)) # prints 0.125
我認為它完全相同(當然除了它更長):
f = lambda x: (1).__truediv__(x)
print(f(8)) # prints 0.125 or 1//8
但我的問題是:
這可能會在將來為我節省很多代碼...... :)
你有一個沒有尾隨零的float
文字,然后你可以訪問__truediv__
方法。 它本身不是一個運營商; 第一個點是浮點值的一部分,第二個點是訪問對象屬性和方法的點運算符。
您可以通過執行以下操作達到相同的目標。
>>> f = 1.
>>> f
1.0
>>> f.__floordiv__
<method-wrapper '__floordiv__' of float object at 0x7f9fb4dc1a20>
另一個例子
>>> 1..__add__(2.)
3.0
這里我們添加1.0到2.0,顯然產生3.0。
問題已經得到了充分的回答(即@Paul Rooney的答案),但也可以驗證這些答案的正確性。
讓我回顧一下現有的答案: ..
不是單一的語法元素!
您可以檢查源代碼是如何“標記化”的 。 這些標記表示代碼的解釋方式:
>>> 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__'),
...]
所以字符串1.
被解釋為數字,第二個.
是一個OP(一個運算符,在本例中是“get attribute”運算符), __truediv__
是方法名稱。 所以這只是訪問float 1.0
的__truediv__
方法。
查看生成的字節碼的另一種方法是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
基本上說的一樣。 它加載常量1.0
的屬性__truediv__
。
關於你的問題
如何在更復雜的陳述中使用它(如果可能的話)?
盡管有可能你永遠不應該編寫這樣的代碼,只是因為它不清楚代碼在做什么。 所以請不要在更復雜的陳述中使用它。 我甚至會走得太遠以至於你不應該在如此“簡單”的陳述中使用它,至少你應該使用括號來分離指令:
f = (1.).__truediv__
這肯定會更具可讀性 - 但有些東西:
from functools import partial
from operator import truediv
f = partial(truediv, 1.0)
會更好!
使用partial
的方法也保留了python的數據模型 ( 1..__truediv__
方法不會!),這可以通過這個小片段來證明:
>>> 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
這是因為1. / (1+2j)
不是由float.__truediv__
評估的,而是complex.__rtruediv__
- operator.truediv
確保在正常操作返回NotImplemented
時調用反向操作但是當你沒有這些后退時直接對__truediv__
進行操作。 這種“預期行為”的喪失是你(通常)不應該直接使用魔法的主要原因。
兩個點在一起起初可能有點尷尬:
f = 1..__truediv__ # or 1..__div__ for python 2
但它和寫作一樣:
f = 1.0.__truediv__ # or 1.0.__div__ for python 2
因為float
文字可以用三種形式編寫:
normal_float = 1.0
short_float = 1. # == 1.0
prefixed_float = .1 # == 0.1
什么是
f = 1..__truediv__
?
f
是一個值為1的float上的綁定特殊方法。 特別,
1.0 / x
在Python 3中,調用:
(1.0).__truediv__(x)
證據:
class Float(float):
def __truediv__(self, other):
print('__truediv__ called')
return super(Float, self).__truediv__(other)
和:
>>> one = Float(1)
>>> one/2
__truediv__ called
0.5
如果我們這樣做:
f = one.__truediv__
我們保留一個綁定到該綁定方法的名稱
>>> f(2)
__truediv__ called
0.5
>>> f(3)
__truediv__ called
0.3333333333333333
如果我們在緊密的循環中進行虛線查找,這可以節省一點時間。
我們可以看到解析表達式的AST告訴我們我們在浮點數上得到__truediv__
屬性, 1.0
:
>>> import ast
>>> ast.dump(ast.parse('1..__truediv__').body[0])
"Expr(value=Attribute(value=Num(n=1.0), attr='__truediv__', ctx=Load()))"
您可以從以下方式獲得相同的結果函數:
f = float(1).__truediv__
要么
f = (1.0).__truediv__
我們也可以通過演繹到達那里。
讓我們建立它。
1本身就是一個int
:
>>> 1
1
>>> type(1)
<type 'int'>
1是浮動后的一段時間:
>>> 1.
1.0
>>> type(1.)
<type 'float'>
下一個點本身就是一個SyntaxError,但它在float的實例上開始一個虛線查找:
>>> 1..__truediv__
<method-wrapper '__truediv__' of float object at 0x0D1C7BF0>
沒有人提到這個 - 這是浮動的一個“綁定方法” , 1.0
:
>>> f = 1..__truediv__
>>> f
<method-wrapper '__truediv__' of float object at 0x127F3CD8>
>>> f(2)
0.5
>>> f(3)
0.33333333333333331
我們可以更加可讀地完成相同的功能:
>>> def divide_one_by(x):
... return 1.0/x
...
>>> divide_one_by(2)
0.5
>>> divide_one_by(3)
0.33333333333333331
divide_one_by
函數的缺點是它需要另一個Python堆棧幀,使其比綁定方法慢一些:
>>> 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]
當然,如果你可以使用普通文字,那就更快了:
>>> def f_3():
... for x in range(1, 11):
... 1.0/x
...
>>> timeit.repeat(f_3)
[2.1224895628296281, 2.1219930218637728, 2.1280188256941983]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.