What is `1..__truediv__` ? Does Python have a .. ("dot dot") notation syntax?

15,846

Solution 1

What you have is a float literal without the trailing zero, which you then access the __truediv__ method of. It's not an operator in itself; the first dot is part of the float value, and the second is the dot operator to access the objects properties and methods.

You can reach the same point by doing the following.

>>> f = 1.
>>> f
1.0
>>> f.__floordiv__
<method-wrapper '__floordiv__' of float object at 0x7f9fb4dc1a20>

Another example

>>> 1..__add__(2.)
3.0

Here we add 1.0 to 2.0, which obviously yields 3.0.

Solution 2

The question is already sufficiently answered (i.e. @Paul Rooneys answer) but it's also possible to verify the correctness of these answers.

Let me recap the existing answers: The .. is not a single syntax element!

You can check how the source code is "tokenized". These tokens represent how the code is interpreted:

>>> 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__'),
 ...]

So the string 1. is interpreted as number, the second . is an OP (an operator, in this case the "get attribute" operator) and the __truediv__ is the method name. So this is just accessing the __truediv__ method of the float 1.0.

Another way of viewing the generated bytecode is to disassemble it. This actually shows the instructions that are performed when some code is executed:

>>> 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

Which basically says the same. It loads the attribute __truediv__ of the constant 1.0.


Regarding your question

And how can you use it in a more complex statement (if possible)?

Even though it's possible you should never write code like that, simply because it's unclear what the code is doing. So please don't use it in more complex statements. I would even go so far that you shouldn't use it in so "simple" statements, at least you should use parenthesis to separate the instructions:

f = (1.).__truediv__

this would be definetly more readable - but something along the lines of:

from functools import partial
from operator import truediv
f = partial(truediv, 1.0)

would be even better!

The approach using partial also preserves python's data model (the 1..__truediv__ approach does not!) which can be demonstrated by this little snippet:

>>> 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 but you don't have these fallbacks when you operate on __truediv__ directly. This loss of "expected behaviour" is the main reason why you (normally) shouldn't use magic methods directly.

Solution 3

Two dots together may be a little awkward at first:

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

But it is the same as writing:

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

Because float literals can be written in three forms:

normal_float = 1.0
short_float = 1.  # == 1.0
prefixed_float = .1  # == 0.1

Solution 4

What is f = 1..__truediv__?

f is a bound special method on a float with a value of one. Specifically,

1.0 / x

in Python 3, invokes:

(1.0).__truediv__(x)

Evidence:

class Float(float):
    def __truediv__(self, other):
        print('__truediv__ called')
        return super(Float, self).__truediv__(other)

and:

>>> one = Float(1)
>>> one/2
__truediv__ called
0.5

If we do:

f = one.__truediv__

We retain a name bound to that bound method

>>> f(2)
__truediv__ called
0.5
>>> f(3)
__truediv__ called
0.3333333333333333

If we were doing that dotted lookup in a tight loop, this could save a little time.

Parsing the Abstract Syntax Tree (AST)

We can see that parsing the AST for the expression tells us that we are getting the __truediv__ attribute on the floating point number, 1.0:

>>> import ast
>>> ast.dump(ast.parse('1..__truediv__').body[0])
"Expr(value=Attribute(value=Num(n=1.0), attr='__truediv__', ctx=Load()))"

You could get the same resulting function from:

f = float(1).__truediv__

Or

f = (1.0).__truediv__

Deduction

We can also get there by deduction.

Let's build it up.

1 by itself is an int:

>>> 1
1
>>> type(1)
<type 'int'>

1 with a period after it is a float:

>>> 1.
1.0
>>> type(1.)
<type 'float'>

The next dot by itself would be a SyntaxError, but it begins a dotted lookup on the instance of the float:

>>> 1..__truediv__
<method-wrapper '__truediv__' of float object at 0x0D1C7BF0>

No one else has mentioned this - This is now a "bound method" on the float, 1.0:

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

We could accomplish the same function much more readably:

>>> def divide_one_by(x):
...     return 1.0/x
...     
>>> divide_one_by(2)
0.5
>>> divide_one_by(3)
0.33333333333333331

Performance

The downside of the divide_one_by function is that it requires another Python stack frame, making it somewhat slower than the bound method:

>>> 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]

Of course, if you can just use plain literals, that's even faster:

>>> def f_3():
...     for x in range(1, 11):
...         1.0/x
...         
>>> timeit.repeat(f_3)
[2.1224895628296281, 2.1219930218637728, 2.1280188256941983]
Share:
15,846

Related videos on Youtube

Taku
Author by

Taku

(Your about me is currently blank) Click here to edit

Updated on June 14, 2022

Comments

  • Taku
    Taku almost 2 years

    I recently came across a syntax I never seen before when I learned python nor in most tutorials, the .. notation, it looks something like this:

    f = 1..__truediv__ # or 1..__div__ for python 2
    
    print(f(8)) # prints 0.125 
    

    I figured it was exactly the same as (except it's longer, of course):

    f = lambda x: (1).__truediv__(x)
    print(f(8)) # prints 0.125 or 1//8
    

    But my questions are:

    • How can it do that?
    • What does it actually mean with the two dots?
    • How can you use it in a more complex statement (if possible)?

    This will probably save me many lines of code in the future...:)

    • tobias_k
      tobias_k about 7 years
      Note: (1).__truediv__ is not really the same as 1..__truediv__, as the former calls int.__truediv__ while the latter does float.__truediv__. Alternatively, you can also use 1 .__truediv__ (with a space)`
    • mkrieger1
      mkrieger1 about 7 years
      Note that 1//8 is 0, not 0.125, in either version of Python.
    • Dunno
      Dunno about 7 years
      reminds me of if (x <- 3) {...}
    • Wheat Wizard
      Wheat Wizard about 7 years
      Here is an example of this in use.
    • Random832
      Random832 about 7 years
      If you're going to write a lambda, write lambda x: 1/x, which is a character shorter (the same length if you need 1./x) Anything that involves explicitly calling a __method__ (outside a derived class that's overriding such a method) probably falls into the category "stupid python tricks" - fun to reason about, but almost certainly doesn't belong in production code.
    • KeithC
      KeithC almost 7 years
      @PeterWood Please can you give an explanation with your comment so the reasons why this shouldnt be done are understandable?
    • Peter Wood
      Peter Wood almost 7 years
      @KeithC The high quality answers and comments show the sample code needs insight to comprehend, is surprising to many, has alternatives which are clearer, more general, and at least as efficient. My main gripe is that readability counts. Save cleverness for where it's most needed - communicating with humans.
  • TemporalWolf
    TemporalWolf about 7 years
    So what we found is a dev who sacrificed a lot of clarity for a little brevity and here we are.
  • Alex Hall
    Alex Hall about 7 years
    This is surprising, why are these valid syntax but 1.__truediv__ is not?
  • tobias_k
    tobias_k about 7 years
    @AlexHall See here. The . seems to be parsed as part of the number, and then the . for the method accessor is missing.
  • DrMcCleod
    DrMcCleod about 7 years
    But since it is awkward and unclear syntax, it should probably be avoided.
  • Thomas Ayoub
    Thomas Ayoub about 7 years
    Maybe that someone is saving his source code to 5.5" floppy disk?
  • jjmontes
    jjmontes about 7 years
    @ThomasAyoub it'd be 5.25" iirc ;-)
  • Superbest
    Superbest about 7 years
    I don't get how this is saving brevity. Isn't 1.0+2 shorter?
  • Brian McCutchon
    Brian McCutchon about 7 years
    @TemporalWolf He may have found it in this recent code golf submission.
  • Derek 朕會功夫
    Derek 朕會功夫 about 7 years
    Fun fact, you can also do this in JavaScript: 1..toString()
  • Nic
    Nic about 7 years
    @BrianMcCutchon Whoops, I was confusing 1+2 and .1+.2. My bad.