什么是 1...__truediv__ ? Python 是否有 ..(“点点”)符号语法?

新手上路,请多包涵

我最近遇到了一个我以前在学习 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

但我的问题是:

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

这可能会在未来为我节省很多行代码……:)

原文由 Taku 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.1k
2 个回答

您拥有的是 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 发布,翻译遵循 CC BY-SA 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(一个运算符,在本例中是“获取属性”运算符)和 __truediv__ 是方法名。所以这只是访问浮点数的 __truediv__ 方法 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

这基本上是一样的。它加载常量 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

This is because 1. / (1+2j) is not evaluated by float.__truediv__ but with complex.__rtruediv__ - operator.truediv makes sure the reverse operation is called when the normal operation returns NotImplemented 但是当你直接操作 __truediv__ 时你没有这些后备。这种“预期行为”的损失是您(通常)不应该直接使用魔术方法的主要原因。

原文由 MSeifert 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题