Why the 'self' argument?

Bengt Richter 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 216.39.172.122...
>> On Fri, 5 Sep 2003 20:15:00 -0400, "John Roth" <newsgroups at jhrothjr.com>
>wrote:
>>
>> >
>> >"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')
>>  ('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')
>>  ('hello',)
>>
>> I.e., the method-vs-function distinction is a matter of how you access the
>function dynamically,
>> 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
example:

 >>> 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()
 This uses
 a_self=<__main__.A object at 0x008F9BF0> and
 b_self=<__main__.B object at 0x008F9FF0>
 >>> b2 = B()
 >>> print b2.bar()
 This uses
 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[0] 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)
 ...
 >>> foo(2,3)
 x=2, y=3, x*y=6
 >>> foo(2,'3')
 x=2, y='3', x*y='33'

 >>> bmeth = foo.__get__(2)
 >>> bmeth(7)
 x=2, y=7, x*y=14
 >>> bmeth('7')
 x=2, y='7', x*y='77'

 >>> foo.__get__(5)('X')
 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 ;-/

Regards,
Bengt Richter




More information about the Python-list mailing list