Sunday, August 9, 2015

Object attribute spacing, floats, and .real, a Python WAT

Recently, while code reviewing a colleague's code, I spotted something like this:
object  .  attribute

I thought 'surely that won't work'. So I went to ipython and did something like this:

In [52]: class MyClass():
   ....:     def __init__(self, attribute):
   ....:         self.attribute = attribute

In [53]: my_object = MyClass(3)

In [54]: my_object.attribute
Out[54]: 3

In [55]: my_object  .  attribute
Out[55]: 3

In [56]: my_object.attribute == my_object . attribute
Out[56]: True

Given that it appeared that python allowed any number of spaces on either side of the full stop (period, if you're speaking American English), which was news to me, I tweeted this:

The ever helpful @brandon_rhodes checked and couldn't see anyplace in pep8 that disallowed it.
But I recommended that my colleague get rid of it anyway because it looked a bit odd.

A few days later I got another reply from @bmispelon

I would have expected the first two would return the same thing and the last one would give a syntax error. But in the first and third statements the full stop is interpreted as a floating point operator, so the first one gives a syntax error, the second gives 1, and the last one gives 1.0.

Knowing all that, can you predict which of the following will give syntax errors, and what the others evaluate to? I suggest using the python interpreter or ipython to check your answers. Have fun!

1) 1.real
2) 1 .real
3) 1. real
4) 1 . real
5) 1..real
6) 1 ..real
7) 1.. real
8) 1 .. real
9) 1. .real
10) 1.     .real
11) 1 . . real

- The full stop in python is both the attribute access operator and the float operator.
- The attribute access operator can have any number of spaces on either side of it.
- The float operator cannot have any spaces on either side of it.
- If there is a full stop immediately after an integer, Python will interpret it as the float operator, even if there are no numbers after it, even if you meant it to be an access operator.


  1. I'm really just a dabbler in Python -- though please don't tell anyone reading my CV that -- so this may not be phrase in correctly Pythonic lingo. And is very unlikely to be on the basis of actual Pythonic knowledge!

    My expectation (AKA guess) would have been that attribute-access and floating points wouldn't be "operators" as such, but lexical-level features. In that light, the example you started with, the heavily-spaced record-dot syntax, would have been especially disconcerting to me -- though I suspect it would be OK (albeit weird-looking) in various old-school procedural languages.

    There are languages in which "." *would* would a feasible operator name, though... over and above the float/qualified name thingie. Admittedly not an OO language, though, in which attribute-accessing otherwise innocent entities like integers isn't such a clear and present danger.

    1. That's a really good point. I'll put my hands up and admit that I was kinda flailing for good names for them, and concluded that calling them operators would get my point across. It's quite hard to google '.'. And any conversation I've had in person about them calls them dots, full stops, or periods, but I was looking for a way to clarify the difference.

      Yeah, everything in Python is an object, so anything.thing always has meaning (though it will throw an error if anything doesn't have a thing). And, apparently, sometimes that meaning might be ambiguous!

    2. It did. I'd have called it the "dot lexeme" (or just character, where it's part of a longer lexeme), but that doesn't seemed to google very well either for Python, so it might indeed not be very "Pythonic", if it doesn't follow the trad lexer/parser/type checker pattern.