Why the 'self' argument?
bokr at oz.net
Sat Sep 6 23:56:48 CEST 2003
On Sat, 6 Sep 2003 14:45:19 -0400, "John Roth" <newsgroups at jhrothjr.com> wrote:
>"Bengt Richter" <bokr at oz.net> wrote in message
>news:bjd20a$sf6$0 at 184.108.40.206...
>> On Fri, 5 Sep 2003 20:15:00 -0400, "John Roth" <newsgroups at jhrothjr.com>
>> >"Grant Edwards" <grante at visi.com> wrote in message
>> >news:3f591d28$0$175$a1866201 at newsreader.visi.com...
>> >> In article <vli387c9mm140a at news.supernews.com>, John Roth wrote:
>> >> >> So that there's no difference between a function and a method.
>> >> >>
>> >> >> Simplicity and orthogonality are good things -- despite what
>> >> >> C++ proponents thing.
>> >> >>
>> >> >> > Hence my comment that requiring it is more complex than not
>> >> >> > requiring it.
>> >> >>
>> >> >> No, it's more complex the Java/Ruby way, since you have to have
>> >> >> two sets of rules for what a name means depending on whether
>> >> >> you're inside a "normal" function or a method. In Python
>> >> >> there's just one set of rules -- mostly.
>> >> >
>> >> > As I said earlier, it's quite possible to define it so that there
>> >> > is always an instance of some kind; whether that's an instance
>> >> > a class or the module itself.
>> >> I don't follow. You're talking about defining a keyword that
>> >> always refers to the first parameter passed to a function? And
>> >> the declared parameters would refer to the 2nd through Nth
>> >> parameters? What if the keyword isn't used in the function
>> >> definition, then do the delcared parameters refer to the 1st
>> >> through Nth?
>> >When Python invokes a function, it binds the operands in the
>> >function call to the identifiers named in the function / method
>> >header. If this is a method, it binds the instance (which is not
>> >in the invocation parameter list, btw.) to the first
>> >parameter in the method header.
>> >If you make "self" a reserved word, then all it has to do
>> >is bind the instance to "self," rather than the first identifier
>> >in the parameter list.
>> >In current python, there are two classes of functions (module
>> >level and embedded) and three classes of methods (instance,
>> >class and static.) Instance methods get the current instance,
>> >class methods get the class object as the instance, and the other
>> >three categories get nothing.
>> >As a separate suggestion, I'd have Python bind the module
>> >object to "self" for module functions and static methods. I
>> >haven't figured out what I want done with embedded methods
>> >and unbound methods yet. Notice that this eliminates the
>> >need for the global statement for module functions - all
>> >identifiers are accessible for rebinding through "self."
>> >> > I think my comments have shown that you can reduce the amount
>> >> > of scoping / name space rules noticably.
>> >> Compared to what? It sure sounds like you're introducing more
>> >> rules than there are now. How would you propose reducing the
>> >> number of rules?
>> >If you don't have to write "self" as the first parameter of a method,
>> >that reduces the complexity. Everything else remains the same.
>> Will this still be possible?
>> >>> def foo(*args): print args
>> >>> class A(object): pass
>> >>> class B(A): pass
>> >>> a = A()
>> >>> b = B()
>> >>> A.bar = foo
>> >>> b.bar('howdy')
>> (<__main__.B object at 0x00906E70>, 'howdy')
>> >>> a.bar('howdy')
>> (<__main__.A object at 0x00907170>, 'howdy')
>> >>> foo('howdy')
>> >>> A.bar('hello')
>> Traceback (most recent call last):
>> File "<stdin>", line 1, in ?
>> TypeError: unbound method foo() must be called with A instance as first
>argument (got str instan
>> ce instead)
>> >>> A.__dict__['bar']('hello')
>> I.e., the method-vs-function distinction is a matter of how you access the
>> not how you define it. There is no static distinction in functionality,
>AFAIK (though I guess
>> there could possibly be some speculative optimizations).
>It certainly needs to be. One of the reasons I haven't written a PEP is
>that providing an instance to an unbound method is a case I don't have
>a clear and simple answer to at the moment.
> (The above doesn't even get into the multiple name issues I alluded to).
>I don't think the multiple name issues are relevant - that has to do with
>how the functions/methods are invoked, rather than what's in the header.)
I think we may be thinking of different things. Here is an admittedly artificial
>>> class A(object):
... def foo(a_self, *args):
... class B(object):
... def bar(b_self, *args):
... return 'This uses\na_self=%r and\nb_self=%r' % (a_self, b_self)
... return B
>>> a = A()
>>> B = a.foo()
>>> b = B()
>>> print b.bar()
a_self=<__main__.A object at 0x008F9BF0> and
b_self=<__main__.B object at 0x008F9FF0>
>>> b2 = B()
>>> print b2.bar()
a_self=<__main__.A object at 0x008F9BF0> and
b_self=<__main__.B object at 0x00903110>
If there were only one "self" keyword to use, you'd have to alias it explicitly
in the outer scope at least (a_self = self before the class B definition in the above).
Hm, I guess that part wouldn't be so bad, actually ;-)
Every function would be an unbound method with a potential undeclared local "self" binding,
and ordinary functions would of course not normally access "self", which would be a local
name error if it did so without a binding (though that brings up another issue: if functions
were to be instances of a function class -- how would you distinguish the function instance
"self" from object instance "self" if the function were playing the function role of a bound method?)
One problem would be breakage in code written with (*args) for parameters, expecting
args to be "self" in bound method context.
For passing an instance to a function to make a bound method call, you wouldn't use
the fist slot in the parameter list, you'd have to use a mechanism like what is already
available, modified to bind to the implicit self instead of the first arg list parameter.
I.e., we now have:
>>> def foo(x, y): print 'x=%r, y=%r, x*y=%r'%(x,y, x*y)
x=2, y=3, x*y=6
x=2, y='3', x*y='33'
>>> bmeth = foo.__get__(2)
x=2, y=7, x*y=14
x=2, y='7', x*y='77'
x=5, y='X', x*y='XXXXX'
So presumably foo.__get__(instance)(argx) would be the way to pass an instance to
an unbound method/function defined as, e.g., def foo(x): print 'self=%r, x=%r' % (self, x).
There'd be an awful lot of code to change though ;-/
More information about the Python-list