What is `1..__truediv__` ? Does Python have a .. (“dot dot”) notation syntax?
最近,我遇到了一种在学习python之前从未见过的语法,在大多数教程中,
1 2 3 | f = 1..__truediv__ # or 1..__div__ for python 2 print(f(8)) # prints 0.125 |
我发现它和(当然,它更长)完全一样:
1 2 | f = lambda x: (1).__truediv__(x) print(f(8)) # prints 0.125 or 1//8 |
但是我的问题是:
- 它怎么做呢?
- 这两个点实际上意味着什么?
- 如何在更复杂的语句中使用它(如果可能)?
将来可能会为我节省很多代码行... :)
您拥有的是一个
您可以通过执行以下操作达到相同的目的。
1 2 3 4 5 | >>> f = 1. >>> f 1.0 >>> f.__floordiv__ <method-wrapper '__floordiv__' of float object at 0x7f9fb4dc1a20> |
另一个例子
1 2 | >>> 1..__add__(2.) 3.0 |
在这里,我们将1.0加到2.0,显然得出3.0。
该问题已经得到足够的答案(即@Paul Rooneys的答案),但也可以验证这些答案的正确性。
让我回顾一下现有的答案:
您可以检查源代码是如何"标记"的。这些标记表示代码的解释方式:
1 2 3 4 5 6 7 8 9 10 | >>> 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 2 3 4 5 6 7 8 9 | >>> 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 |
基本上说的一样。它加载常量
关于你的问题
And how can you use it in a more complex statement (if possible)?
即使您可能永远也不要这样写代码,只是因为不清楚代码在做什么。因此,请不要在更复杂的语句中使用它。我什至会走得更远,以至于您不应该在如此"简单"的语句中使用它,至少您应该使用括号将指令分开:
1 | f = (1.).__truediv__ |
这肯定会更具可读性-但类似于:
1 2 3 | from functools import partial from operator import truediv f = partial(truediv, 1.0) |
会更好!
使用
1 2 3 4 5 6 7 8 9 10 11 12 | >>> 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 | f = 1..__truediv__ # or 1..__div__ for python 2 |
但这与写作相同:
1 | f = 1.0.__truediv__ # or 1.0.__div__ for python 2 |
因为
1 2 3 | normal_float = 1.0 short_float = 1. # == 1.0 prefixed_float = .1 # == 0.1 |
What is
f = 1..__truediv__ ?
1 | 1.0 / x |
在Python 3中,调用:
1 | (1.0).__truediv__(x) |
证据:
1 2 3 4 | class Float(float): def __truediv__(self, other): print('__truediv__ called') return super(Float, self).__truediv__(other) |
和:
1 2 3 4 | >>> one = Float(1) >>> one/2 __truediv__ called 0.5 |
如果这样做:
1 | f = one.__truediv__ |
我们保留绑定到该绑定方法的名称
1 2 3 4 5 6 | >>> f(2) __truediv__ called 0.5 >>> f(3) __truediv__ called 0.3333333333333333 |
如果我们在一个紧密的循环中执行该点分查找,则可以节省一些时间。
解析抽象语法树(AST)
我们可以看到,解析AST的表达式可以告诉我们,我们在浮点数
1 2 3 | >>> import ast >>> ast.dump(ast.parse('1..__truediv__').body[0]) "Expr(value=Attribute(value=Num(n=1.0), attr='__truediv__', ctx=Load()))" |
您可以从以下获得相同的结果函数:
1 | f = float(1).__truediv__ |
要么
1 | f = (1.0).__truediv__ |
扣除
我们也可以通过扣除到达那里。
让我们建立它。
1本身是
1 2 3 4 | >>> 1 1 >>> type(1) <type 'int'> |
1,之后是句点:
1 2 3 4 | >>> 1. 1.0 >>> type(1.) <type 'float'> |
下一个点本身就是SyntaxError,但它会在float实例上开始点分查找:
1 2 | >>> 1..__truediv__ <method-wrapper '__truediv__' of float object at 0x0D1C7BF0> |
没有人提到这-这是浮点数
1 2 3 4 5 6 7 | >>> f = 1..__truediv__ >>> f <method-wrapper '__truediv__' of float object at 0x127F3CD8> >>> f(2) 0.5 >>> f(3) 0.33333333333333331 |
我们可以更容易地完成相同的功能:
1 2 3 4 5 6 7 | >>> def divide_one_by(x): ... return 1.0/x ... >>> divide_one_by(2) 0.5 >>> divide_one_by(3) 0.33333333333333331 |
性能
1 2 3 4 5 6 7 8 9 10 11 12 | >>> 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] |
当然,如果您仅可以使用普通文字,那就更快了:
1 2 3 4 5 6 | >>> def f_3(): ... for x in range(1, 11): ... 1.0/x ... >>> timeit.repeat(f_3) [2.1224895628296281, 2.1219930218637728, 2.1280188256941983] |