[Python-ideas] Mitigating 'self.' Method Pollution

Nick Coghlan ncoghlan at gmail.com
Sat Jul 11 11:12:22 CEST 2015


On 11 July 2015 at 18:33, Ben Finney <ben+python at benfinney.id.au> wrote:
> Michael Hewitt <michael at hewitts.us> writes:
>
>> Is anyone actually going to argue that the first version is cleaner
>> and more readable than the second? All I see when I read the first is
>> 'self', 'self', 'self' -- my son's exact words to me last night.
>
> That's a pity. You can choose a different word if you like.
>
>> As far as maintainability, the author of the first version must
>> repeatedly make the same decisions over and over regarding how to
>> scope each variable reference as he/she types 'y', 'gravity', and
>> 'height'.
>
> Another way to say that is that the author must *explicitly
> communicate*, to the reader, the namespace of each name within the
> function scope.
>
> don't make the mistake of attempting to optimise time spent typing in
> code, versus time spent comprehending the code as a reader. The latter
> far outweighs the former, and is the preferred target for optimisation.

That's not the way I read Michael's proposal. I read his proposal as
aiming to cut down on visual *noise* when *reading*.

It's worthwhile to compare the different examples in terms of
"characters conveying useful information":

    def keep_moving_gravity(self):
        self.y += self.gravity
        self.y = max(self.y, 0)
        self.y = min(self.y, height - 1)

Here, self is repeated 7 times: 28 characters. Discounting indentation
and line breaks, the whole function is only 96 characters long, so
almost a third of the function definition consists of the word "self".

With Michael's proposal, that changes to:

    def keep_moving_gravity(self):
        self y, gravity
        y += gravity
        y = max(y, 0)
        y = min(y, height - 1)

Total character length is now 79 characters, with 15 characters
devoted to the "self y, gravity" declarative element. That's a worthy
reduction in visual scoping noise (to less than 20%), but it's come at
the cost of losing actual signal: there's no longer a local marker on
the references to "y" and "gravity" to say they're instance variables
rather than local variables.

That's also still worse than the best current code can already do
(without breaking the "self" convention) which is to assign a
frequently accessed object to a single character variable name:

    def keep_moving_gravity(self):
        s = self
        s.y += s.gravity
        s.y = max(s.y, 0)
        s.y = min(s.y, height - 1)

That keeps all of the relevant attribute lookup signal, and is only 14
characters of scoping noise (8 for the "s = self", 6 for the single
character object references) out of 86 total characters.

Finally, an "implied reference to first parameter" for attribute
lookups inside functions offers the most minimalist proposal I can
think of that would still be easy to read from a semantic perspective:

    def keep_moving_gravity(self):
        .y += .gravity
        .y = max(.y, 0)
        .y = min(.y, height - 1)

We've reduced the function length to only 72 characters, and it's 100%
signal. The only thing we've lost relative to the original is the 6
explicit references to "self", which are now implied by looking up an
attribute without specifying a source object.

If we *did* do something like that last example (which I'm not
convinced is a good idea, given that the "n = name" trick works for
*any* frequently referenced variable, whether inside a function or
not), we'd need to think seriously about the implications for implicit
scopes, like those created for generator expressions and container
comprehensions. At the moment, their first argument is the outermost
iterable, which would become accessible within the expression body if
there was a syntax permitting implied references for attribute
lookups.

Regards,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-ideas mailing list