32
votes

So I saw these two questions on twitter. How is 1.real a syntax error but 1 .real is not?

>>> 1.real
  File "<stdin>", line 1
    1.real
         ^
SyntaxError: invalid syntax
>>> 1 .real
1
>>> 1. real
  File "<stdin>", line 1
    1. real
          ^
SyntaxError: invalid syntax
>>> 1 . real
1
>>> 1..real
1.0
>>> 1 ..real
  File "<stdin>", line 1
    1 ..real
       ^
SyntaxError: invalid syntax
>>> 1.. real
1.0
>>> 1 .. real
  File "<stdin>", line 1
    1 .. real
       ^
SyntaxError: invalid syntax
3
I guess 1. is greedily parsed to a float number, and then real is missing the ., whereas 1 .real parses 1 as an int. Spaces are not allowed around decimal point, but you can have spaces before and after 'method accessor dot' (for lack of a better name). - tobias_k
try: (1).real or 1.0.real or 1..real (they are all the same) - fferri
There are arguments to change this behavior, but if you make 1.real an attribute access, then 1.e4 looks like an attribute access, and perfectly fine code that used to produce 10000.0 suddenly produces an AttributeError. It's not worth the hassle. I'm pretty sure Ruby allows this kind of access, since ints have more interesting methods over there; I'm not sure what they do to handle it. - user2357112 supports Monica
Aside: Please try to avoid clickbait question titles -- that is to say, please try to add enough information that someone can have a reasonable idea of whether a question is interesting or applicable to them before they click to open it. I've attempted to edit this to do so here. - Charles Duffy

3 Answers

56
votes

I guess that the . is greedily parsed as part of a number, if possible, making it the float 1., instead of being part of the method call.

Spaces are not allowed around the decimal point, but you can have spaces before and after the . in a method call. If the number is followed by a space, the parse of the number is terminated, so it's unambiguous.

Let's look at the different cases and how they are parsed:

>>> 1.real    # parsed as (1.)real  -> missing '.'
>>> 1 .real   # parsed as (1).real  -> okay
>>> 1. real   # parsed as (1.)real  -> missing '.'
>>> 1 . real  # parsed as (1).real  -> okay
>>> 1..real   # parsed as (1.).real -> okay
>>> 1 ..real  # parsed as (1)..real -> one '.' too much
>>> 1.. real  # parsed as (1.).real -> okay
>>> 1 .. real # parsed as (1)..real -> one '.' too much
11
votes

With 1.real Python is looking for a floating-point numeric literal like 1.0 and you can't have an r in a float. With 1 .real Python has taken 1 as an integer and is doing the attribute lookup on that.

It's important to note that the floating-point syntax error handling happens before the . attribute lookup.

1
votes

1 .real works because it is the attribute .real called on the integer 1.

1.real does not work, because you imperatively need a space at the end of a float. Otherwise it is a syntax error.