[Edu-sig] Ditch "self"?

Paul D. Fernhout pdfernhout at kurtz-fernhout.com
Wed Oct 24 04:36:11 CEST 2007


Kirby-

Whitespace clearly removes redundant clutter (either braces or begin/end);
all complex human-edited programs need to be indented for readability anyway.

The redundant "self" in Python method/function definitions mainly adds
clutter IMHO, and so is a wart IMHO (even as it is sometimes changed when
used in class methods, and so on). Smalltalk managed to get by with "self"
being implicit, and that language definitely has people reading code
sometimes with the kind of "self" projection you mention.

Conceptually, I feel that since globals are not passed in explicitly into
functions, "self" should not be either. The word "self" not a global of
course, and nor it is reserved in Python (unlike Smalltalk), but it still
feels to me that "self" is more a part of an active function's namespace
more than it is a calling parameter IMHO.

But I can also see the arguments to have it be explicit, including the ones
you mention. I specially like the Unicode name change one for people coding
in a language other than English, but even then, there are so many other
English words you can't change in Python statements, so does it really get
you very far to be able to change only "self"? It seems like a deeper
multi-language paradigm for programming is needed to address that specific
issue, and that is something, say, the OLPC project is struggling with in
terms of viewing source code written mostly in English.

I do know that in supporting prototypes under Python in PataPata, I did
wrestle with issues where assigning new functions into slots used as methods
was more complex than I naively would have expected.

=== EXAMPLE

For example, when copying a method from one prototype to another, I ended up
with this complex code, both for assignment and calling, using an indirect
PrototypeMethod class as a wrapper for functions:

class PrototypeClass(object):
    ...
    def __setattr__(self, name, value):
          ...
          if callable(value):
            if isinstance(value, PrototypeMethod):
                if value.prototypeHoldingTheFunction != self:
                    wrappedMethod = PrototypeMethod(self, value.function,
value.source)
                    value = wrappedMethod
            else:
                value = PrototypeMethod(self, value)
          ...

class PrototypeMethod:
    ...
    def __call__(self, *args, **kwargs):
        return self.function(self.prototypeHoldingTheFunction, *args, **kwargs)

This was rather than a more simple assign of Python functions. There was an
issue in that the method function was already bound to the specific
prototype instance, rather than being a more generic function as "self" in
the parameter list might imply. Maybe someone who knows more about Python
internals than I could have done this more simply without introducing these
extra classes?

Clearly this works:

$ python
Python 2.4.4 (#2, Apr  5 2007, 20:11:18)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo:
...   def bar(self):
...     print "hello"
...
>>> Foo().bar()
hello
>>> def test(self):
...   print "hello 2"
...
>>> test(None)
hello 2
>>> Foo.bar = test
>>> Foo().bar()
hello 2
>>>
>>> y = Foo()
>>> y.bar = Foo().bar
>>> y.bar()
hello 2
>>>

But this additional code does not work:

>>> x = Foo()
>>> x.bar
<bound method Foo.test of <__main__.Foo instance at 0xf7ce72ec>>
>>> test
<function test at 0xf7ce0bc4>
>>> x.bar = test
>>> x.bar()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: test() takes exactly 1 argument (0 given)
>>>

And it is this second case which I was concerned about in prototypes.
Clearly the two functions are different. One if a "function"; the other is a
"bound method".

It seems to me that if "self" is indeed the same in both functions that the
assign of test to the Foo instance should work the same way the assign of
test to the Foo class does. But it does not, because the assignment to the
instance needs to be with a function which already has "self" bound to the
specific instance. So, I feel like there is a conceptual mismatch here. But
maybe I am just not looking at it the right way?

I certainly understated why, to pass function pointers using "x.bar" to
objects which call them, that "self" needs to be bound. Although I have some
problems with even this, since the function pointer is bound so tightly that
if you completely replace it in the class or instance, the previously bound
version is used, not the new one (unless you just replace code internal to
the function). A common place where this issue comes up is if you build a
GUI which has methods linked to button events, and you want to modify or
reload the class on the fly and have the button activate the new code, not
the old code; you can do it, but it is non-obvious how to do it and involves
mucking about with the internals of functions (as has been discussed here
before). I'd rather see "actionPerformed=x.foo" bind foo more lightly, so
that the actual function was looked up at dispatch time rather than at
assignment time, as a form of late binding.

Even Smalltalk has some issues with this sort of thing, though they are not
exactly the same -- in Smalltalk's case, when you assign a block to be
performed at a GUI action, and you modify the code which defined the block,
typically the new block is not used, just the old block; as long as your
block just does something like "self bar." and you never change the name of
method "bar" you are OK, but if you do, you can have problems. And they can
be confusing if you do not know what to expect.

===

In any case, clearly a major reason I and many others like Python is
precisely the use of significant whitespace; whether "self" is explicit or
not is not would at best be a minor issue for most people. So I myself don't
see that one can completely equate the criticism of the two, based on
significance to the user and the language.

Like the superfluous trailing colon in Python (which personally I don't
really like and find a frequent source of typos), I could see how requiring
"self" might be more a matter of opinion -- some people might think it adds
to readability, some might think it detracts from readability.

But then, I feel the superfluous dot (.) between accessor names (as in
"Foo.bar.baz()" instead of "Foo bar baz()"  also detracts from readability,
:-) but then I like Smalltalk syntax in part for that reason. Python
initially was to be easy for C programmers, and the dot is what C used. It's
part of history now.

I doubt any of those superfluous things, the trailing colon, "self", or the
embedded dot is going to change now in Python (nor probably should it). The
language is what it is -- with strengths and weaknesses, clarities and
warts. I'd rather see core effort go into PyPy, even better IDEs and
libraries, Jython improvements,  and "edit and continue". And maybe improved
multiprocessing support (even if just by libraries). I think the late
binding of function calls against objects might be possible as an
improvement without breaking much of anything else though, but I have not
thought all the potential side effects through for existing Python code.
Those might be more bang for the buck rather than tinkering with major
language features which are pretty much set in stone in the community by now
(whether they are features or "misfeatures").
  http://catb.org/jargon/html/M/misfeature.html
But then, I guess I still like "print", if (unlike Smalltalk) you are going
to have any statements at all. :-)

I know that all will sound funny or even hypocritical coming from someone
who has talked about and worked towards adding improved prototypes support
to Python -- but remember, after our discussions here, I agreed it made
sense not to mess with the Python syntax, unlike, say, the syntax changing
direction that Prothon went in.
  http://blog.ianbicking.org/initial-thoughts-on-prothon.html
Even then, I encountered the issues you rightly pointed out, that if the
base libraries are written in a different style using classes, and all the
error messages are oriented around classes, and all the documentation is
oriented around classes, it is hard for anyone to grok the prototype layer
and use it easily. I think that may be one issue if, say, "print"
disappears. :-)

So, all that is not to say experiments aren't sometimes interesting, of
course, or that they might not eventually lead to new and better things.
Just an issue of priorities, and I can only set them for myself.

--Paul Fernhout

kirby urner wrote:
> Guido: "Personally, I find that the criticism of explicit self has
> about as much merit as the criticism of Python's use of
> whitespace."
> 
> [ http://www.artima.com/weblogs/viewpost.jsp?thread=214325 ]
> 
> Ya, my thought as well.
> 
> Lotsa people with nasty bad Java habits come kibitzing to Python's
> door, expecting to be taken seriously (not! -- except sometimes).
> 
> What I like about 'self', as much documented here (edu-sig), is it
> ain't a key word, i.e. it's a placeholder in which any unicode glyph
> might go.
> 
> And as discussed with John Zelle, I see advantages into projecting
> a sense of one's self into 'self' -- *not* as some exercise in introversion,
> but in order to better empathize with some external knowledge domain
> (dams, horse farms whatever).
> 
> "I am an hydraulic pump, these are my attributes" -- that kind of thing.
> 
> OO's use of "self" inherently promotes empathy *if* the pedagogy
> is skillful, sensitive to future developer responsibilities (and if the
> students are adepts, have true geek potential).
> 
> Believe it or not, all this connects to my math-thinking-l position that
> women clearly control computer science (Ada... Grace Hopper), if
> not other disciplines, and even if guys with big egos like to pretend
> *they* do.
> 
> Kirby
> _______________________________________________
> Edu-sig mailing list
> Edu-sig at python.org
> http://mail.python.org/mailman/listinfo/edu-sig
> 
> 


More information about the Edu-sig mailing list