So you think you can dance with a floating point?
123 is the same part of the object as 3.14 , the “problem” lies in the grammar rules of the language; the parser thinks we're going to define a float - not an int with a method callback.
We get the expected behavior if we wrap the number in brackets, as shown below.
>>> (123).__str__() '123'
Or if we just add spaces after 123 :
>>> 123 .__str__() '123'
The reason it doesn't work for 123.__str__() is because the dot following 123 is interpreted as the decimal point of some partially declared floating point.
>>> 123.__str__() File "", line 1 123.__str__() ^ SyntaxError: invalid syntax
The parser tries to interpret __str__() as a sequence of numbers, but obviously does not work - and we get a SyntaxError, basically saying that the parser stumbled upon something that it did not expect.
Development
When viewing 123.__str__() python analyzer can use either 3 characters and interpret these 3 characters as an integer, or , it can use 4 characters and interpret them as start with a floating point.
123.__str__() ^^^ - int
123.__str__() ^^^^- start of floating-point
Just as a little child would like as much cake as possible on his plate, the parser is greedy and would like to learn as much as he can all at once; even if it’s not always the best of ideas, since the last (“best”) alternative has been chosen.
When he later realizes that __str__() cannot in any way be interpreted as floating point decimal, it is too late; SyntaxError.
Note
123 .__str__()
In the above snippet, 123 (note the space) must be interpreted as an integer, since the number cannot contain spaces. This means that it is semantically equivalent to (123).__str__() .
Note
123..__str__()
The above also works because a number can contain at most one decimal point, which means it is equivalent to (123.).__str__() .
For linguistic lawyers
This section contains the lexical definition of the corresponding literals.
Lexical Analysis - 2.4.5 Floating-Point Literals
floatnumber ::= pointfloat | exponentfloat pointfloat ::= [intpart] fraction | intpart "." exponentfloat ::= (intpart | pointfloat) exponent intpart ::= digit+ fraction ::= "." digit+ exponent ::= ("e" | "E") ["+" | "-"] digit+
Lexical Analysis - 2.4.4 Integer Literals
integer ::= decimalinteger | octinteger | hexinteger | bininteger decimalinteger ::= nonzerodigit digit* | "0"+ nonzerodigit ::= "1"..."9" digit ::= "0"..."9" octinteger ::= "0" ("o" | "O") octdigit+ hexinteger ::= "0" ("x" | "X") hexdigit+ bininteger ::= "0" ("b" | "B") bindigit+ octdigit ::= "0"..."7" hexdigit ::= digit | "a"..."f" | "A"..."F" bindigit ::= "0" | "1"