什么是`1 ..__ truediv__`?Python有一个......("点点")表示法语法吗?

abc*_*ccd 189 python syntax operators python-2.x python-3.x

我最近遇到了一个我以前从未见过的语法,当我学习python时,在大多数教程中都没有,..符号,它看起来像这样:

f = 1..__truediv__ # or 1..__div__ for python 2

print(f(8)) # prints 0.125 
Run Code Online (Sandbox Code Playgroud)

我认为它完全相同(当然除了它更长):

f = lambda x: (1).__truediv__(x)
print(f(8)) # prints 0.125 or 1//8
Run Code Online (Sandbox Code Playgroud)

但我的问题是:

  • 它怎么能这样做?
  • 这两个点实际上意味着什么?
  • 如何在更复杂的声明中使用它(如果可能)?

这可能会在将来为我节省很多代码...... :)

Pau*_*ney 211

你有一个float没有尾随零的文字,然后你可以访问该__truediv__方法.它本身不是一个运营商; 第一个点是浮点值的一部分,第二个点是访问对象属性和方法的点运算符.

您可以通过执行以下操作达到相同的目标.

>>> f = 1.
>>> f
1.0
>>> f.__floordiv__
<method-wrapper '__floordiv__' of float object at 0x7f9fb4dc1a20>
Run Code Online (Sandbox Code Playgroud)

另一个例子

>>> 1..__add__(2.)
3.0
Run Code Online (Sandbox Code Playgroud)

这里我们添加1.0到2.0,显然产生3.0.

  • 所以我们发现的是一个开发人员,为了简洁而牺牲了很多清晰度,我们就是这样. (165认同)
  • 也许有人将他的源代码保存到5.5"软盘? (11认同)
  • @ThomasAyoub它是5.25"iirc ;-) (10认同)
  • @TemporalWolf他可能在[最近的代码高尔夫提交](https://codegolf.stackexchange.com/a/114572/56183)中找到了它. (9认同)
  • 有趣的是,您也可以在JavaScript中执行此操作:`1..toString()` (2认同)

MSe*_*ert 73

问题已经得到了充分的回答(即@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__'),
 ...]
Run Code Online (Sandbox Code Playgroud)

所以字符串1.被解释为数字,第二个.是OP(一个运算符,在本例中是"get attribute"运算符),并且__truediv__是方法名称.所以这只是访问__truediv__float 的方法1.0.

查看生成的字节码的另一种方法是组装它.这实际上显示了执行某些代码时执行的指令: 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
Run Code Online (Sandbox Code Playgroud)

基本上说的一样.它加载__truediv__常量的属性1.0.


关于你的问题

如何在更复杂的陈述中使用它(如果可能的话)?

尽管有可能你永远不应该编写这样的代码,只是因为它不清楚代码在做什么.所以请不要在更复杂的陈述中使用它.我甚至会走得太远以至于你不应该在如此"简单"的陈述中使用它,至少你应该使用括号来分离指令:

f = (1.).__truediv__
Run Code Online (Sandbox Code Playgroud)

这肯定会更具可读性 - 但有些东西:

from functools import partial
from operator import truediv
f = partial(truediv, 1.0)
Run Code Online (Sandbox Code Playgroud)

会更好!

使用的方法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
Run Code Online (Sandbox Code Playgroud)

这是因为1. / (1+2j)没有评估,float.__truediv__但是complex.__rtruediv__- operator.truediv确保在正常操作返回时调用反向操作,NotImplemented但是当您__truediv__直接操作时没有这些后备.这种"预期行为"的丧失是你(通常)不应该直接使用魔法的主要原因.


sob*_*evn 40

两个点一起起初可能有点尴尬:

f = 1..__truediv__ # or 1..__div__ for python 2
Run Code Online (Sandbox Code Playgroud)

但它和写作一样:

f = 1.0.__truediv__ # or 1.0.__div__ for python 2
Run Code Online (Sandbox Code Playgroud)

因为float文字可以用三种形式写成:

normal_float = 1.0
short_float = 1.  # == 1.0
prefixed_float = .1  # == 0.1
Run Code Online (Sandbox Code Playgroud)

  • 但由于语法笨拙且不清楚,因此应该避免使用它. (7认同)
  • @AlexHall见[这里](http://stackoverflow.com/a/31037917/1639625).似乎将`.`解析为数字的一部分,然后缺少方法访问器的`.`. (3认同)

Aar*_*all 11

什么是f = 1..__truediv__

f是一个值为1的float上的绑定特殊方法.特别,

1.0 / x
Run Code Online (Sandbox Code Playgroud)

在Python 3中,调用:

(1.0).__truediv__(x)
Run Code Online (Sandbox Code Playgroud)

证据:

class Float(float):
    def __truediv__(self, other):
        print('__truediv__ called')
        return super(Float, self).__truediv__(other)
Run Code Online (Sandbox Code Playgroud)

和:

>>> one = Float(1)
>>> one/2
__truediv__ called
0.5
Run Code Online (Sandbox Code Playgroud)

如果我们这样做:

f = one.__truediv__
Run Code Online (Sandbox Code Playgroud)

我们保留一个绑定到该绑定方法的名称

>>> f(2)
__truediv__ called
0.5
>>> f(3)
__truediv__ called
0.3333333333333333
Run Code Online (Sandbox Code Playgroud)

如果我们在紧密的循环中进行虚线查找,这可以节省一点时间.

解析抽象语法树(AST)

我们可以看到解析表达式的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()))"
Run Code Online (Sandbox Code Playgroud)

您可以从以下方式获得相同的结果函数:

f = float(1).__truediv__
Run Code Online (Sandbox Code Playgroud)

要么

f = (1.0).__truediv__
Run Code Online (Sandbox Code Playgroud)

扣除

我们也可以通过演绎到达那里.

让我们建立它.

1本身就是int:

>>> 1
1
>>> type(1)
<type 'int'>
Run Code Online (Sandbox Code Playgroud)

1是浮动后的一段时间:

>>> 1.
1.0
>>> type(1.)
<type 'float'>
Run Code Online (Sandbox Code Playgroud)

下一个点本身就是一个SyntaxError,但是它会在float的实例上开始一个虚线查找:

>>> 1..__truediv__
<method-wrapper '__truediv__' of float object at 0x0D1C7BF0>
Run Code Online (Sandbox Code Playgroud)

没有人提到这个 - 这现在是浮动的"绑定方法",1.0:

>>> f = 1..__truediv__
>>> f
<method-wrapper '__truediv__' of float object at 0x127F3CD8>
>>> f(2)
0.5
>>> f(3)
0.33333333333333331
Run Code Online (Sandbox Code Playgroud)

我们可以更加可读地完成相同的功能:

>>> def divide_one_by(x):
...     return 1.0/x
...     
>>> divide_one_by(2)
0.5
>>> divide_one_by(3)
0.33333333333333331
Run Code Online (Sandbox Code Playgroud)

性能

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]
Run Code Online (Sandbox Code Playgroud)

当然,如果你可以使用普通文字,那就更快了:

>>> def f_3():
...     for x in range(1, 11):
...         1.0/x
...         
>>> timeit.repeat(f_3)
[2.1224895628296281, 2.1219930218637728, 2.1280188256941983]
Run Code Online (Sandbox Code Playgroud)