Why the 'self' argument?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Terry Reedy

    #31
    Re: Why the 'self' argument?


    "Harri Pesonen" <fuerte@sci.f i> wrote in message
    news:Ucp6b.3992 $ZB4.3874@reade r1.news.jippii. net...[color=blue]
    > I agree, it's not logical.[/color]

    Do my previous two responses today make the logic any clearer?
    [color=blue]
    > I'm learning Python at the moment, and like it very much.[/color]

    Try learning this. Class K method attribute f with params (s, *rest)
    can be called as K.f(Kinst, *rest), consistent with all other function
    calls.

    Abbreviation Kinst.f(*rest) makes the call look inconsistent by making
    K inplicit (and moving Kinst), but the same inplicitness enables
    runtime method lookup and superclass inheritance. I think the
    benefits that following are worth the minor bump in one's learning
    curve.

    Terry J. Reedy


    Comment

    • Harri Pesonen

      #32
      Re: Why the 'self' argument?

      Grant Edwards wrote:
      [color=blue]
      > In article <Ucp6b.3992$ZB4 .3874@reader1.n ews.jippii.net> , Harri Pesonen wrote:
      >[color=green]
      >>I agree, it's not logical. I'm learning Python at the moment, and like
      >>it very much. This "self" thing seems to be the only odd feature,[/color]
      >
      > It seemed quite natural to me, but perhaps that's because I'd
      > used other languages that worked the same way. Coming from
      > Modula-3 and Smalltalk, the way classes worked in Python seemed
      > quite intuitive.
      >
      > OTOH, C++ seems like a real non-intuitive mess to me.
      >[color=green]
      >>it feels like the whole class feature was added later.[/color]
      >
      > Why?[/color]

      Because everything else in Python seems to be very compact, there are no
      variable type declarations, or variable declarations, or anything else
      unnecessary that can be omitted. I would like to have self omitted, it
      would make the class syntax more beautiful and compact. On the other
      hand, I would like to have real private methods.

      Also I think that the class members should be explicitly declared. In
      general, it would be nice to have an option (like Option Explicit in
      Visual Basic) so that you can't assign to variables that have not been
      declared. It would probably make Python less error prone. Also if
      variable declarations could have the type, again some errors could be
      detected at compile time.

      I have not written any real Python applications yet, so I'm not expert,
      these are just my thoughts at this point.

      Harri

      Comment

      • Dave Brueck

        #33
        Re: Why the 'self' argument?

        On Saturday 06 September 2003 02:17 pm, Terry Reedy wrote:[color=blue]
        > The main virtue of the inst.meth(*rest ) abbreviation, with klass made
        > implicit, is not just to save typing the klass name but to push
        > determination of which 'klass' to the runtime inheritance tree,[/color]

        Pardon me if this was mentioned previously, but another benefit is to simplify
        the process of passing methods or stand-alone functions around - the user of
        such functions doesn't have to differentiate between the two:

        class Foo:
        def OnEvent(self, a, b):
        print 'Method', a, b

        def Listener(a, b):
        print 'Function', a,b

        def SomeEventGenera tor(eventListen er):
        eventListener(5 , 6)

        SomeEventGenera tor doesn't care if the callback is actually a method or a
        function. Both these work just fine:

        f = Foo()
        SomeEventGenera tor(foo.OnEvent )
        SomeEventGenera tor(Listener)

        Obviously, if the shorthand inst.meth(*rest ) wasn't available there would
        probably be another way to do the same thing, but "inst.meth" is concise and
        does what you'd expect.

        -Dave

        Comment

        • Bengt Richter

          #34
          Re: Why the 'self' argument?

          On Sat, 6 Sep 2003 14:45:19 -0400, "John Roth" <newsgroups@jhr othjr.com> wrote:
          [color=blue]
          >
          >"Bengt Richter" <bokr@oz.net> wrote in message
          >news:bjd20a$sf 6$0@216.39.172. 122...[color=green]
          >> On Fri, 5 Sep 2003 20:15:00 -0400, "John Roth" <newsgroups@jhr othjr.com>[/color]
          >wrote:[color=green]
          >>[color=darkred]
          >> >
          >> >"Grant Edwards" <grante@visi.co m> wrote in message
          >> >news:3f591d28$ 0$175$a1866201@ newsreader.visi .com...
          >> >> In article <vli387c9mm140a @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.[/color]
          >>
          >> Will this still be possible?
          >>[color=darkred]
          >> >>> def foo(*args): print args[/color]
          >> ...[color=darkred]
          >> >>> class A(object): pass[/color]
          >> ...[color=darkred]
          >> >>> class B(A): pass[/color]
          >> ...[color=darkred]
          >> >>> a = A()
          >> >>> b = B()
          >> >>> A.bar = foo
          >> >>> b.bar('howdy')[/color]
          >> (<__main__.B object at 0x00906E70>, 'howdy')[color=darkred]
          >> >>> a.bar('howdy')[/color]
          >> (<__main__.A object at 0x00907170>, 'howdy')[color=darkred]
          >> >>> foo('howdy')[/color]
          >> ('howdy',)
          >>[color=darkred]
          >> >>> A.bar('hello')[/color]
          >> Traceback (most recent call last):
          >> File "<stdin>", line 1, in ?
          >> TypeError: unbound method foo() must be called with A instance as first[/color]
          >argument (got str instan[color=green]
          >> ce instead)[color=darkred]
          >> >>> A.__dict__['bar']('hello')[/color]
          >> ('hello',)
          >>
          >> I.e., the method-vs-function distinction is a matter of how you access the[/color]
          >function dynamically,[color=green]
          >> not how you define it. There is no static distinction in functionality,[/color]
          >AFAIK (though I guess[color=green]
          >> there could possibly be some speculative optimizations).[/color]
          >
          >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.
          >[color=green]
          >>[/color]
          > (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.)
          >[/color]
          I think we may be thinking of different things. Here is an admittedly artificial
          example:
          [color=blue][color=green][color=darkred]
          >>> class A(object):[/color][/color][/color]
          ... 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
          ...[color=blue][color=green][color=darkred]
          >>> a = A()
          >>> B = a.foo()
          >>> b = B()
          >>> print b.bar()[/color][/color][/color]
          This uses
          a_self=<__main_ _.A object at 0x008F9BF0> and
          b_self=<__main_ _.B object at 0x008F9FF0>[color=blue][color=green][color=darkred]
          >>> b2 = B()
          >>> print b2.bar()[/color][/color][/color]
          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:
          [color=blue][color=green][color=darkred]
          >>> def foo(x, y): print 'x=%r, y=%r, x*y=%r'%(x,y, x*y)[/color][/color][/color]
          ...[color=blue][color=green][color=darkred]
          >>> foo(2,3)[/color][/color][/color]
          x=2, y=3, x*y=6[color=blue][color=green][color=darkred]
          >>> foo(2,'3')[/color][/color][/color]
          x=2, y='3', x*y='33'
          [color=blue][color=green][color=darkred]
          >>> bmeth = foo.__get__(2)
          >>> bmeth(7)[/color][/color][/color]
          x=2, y=7, x*y=14[color=blue][color=green][color=darkred]
          >>> bmeth('7')[/color][/color][/color]
          x=2, y='7', x*y='77'
          [color=blue][color=green][color=darkred]
          >>> foo.__get__(5)( 'X')[/color][/color][/color]
          x=5, y='X', x*y='XXXXX'

          So presumably foo.__get__(ins tance)(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

          Comment

          • Jonathan Aquino

            #35
            Re: Why the 'self' argument?

            (Paul Foley replied to my e-mail directly. Below are his remarks,
            which I was interested to read. At the end is my reply.)

            [color=blue][color=green]
            >> 1) I abhor redundancy. My first impression on seeing self as the[/color][/color]
            first[color=blue][color=green]
            >> parameter of all method declarations is that it seems redundant --[/color][/color]
            it[color=blue][color=green]
            >> probably isn't technically, but it looks redundant: (self, (self,
            >> (self, (self, ...[/color][/color]

            PF> Of course it's not redundant -- no more than the "x" is redundant
            in

            PF> def ln(x):
            PF> ...

            PF> def sin(x):
            PF> ...

            PF> def cos(x):
            PF> ...

            PF> def tan(x):
            PF> ...

            PF> def cosh(x):
            PF> ...

            PF> def sinh(x):
            PF> ...

            PF> etc., etc. Gee, look: (x, (x, (x, (x, (x, ...
            [color=blue][color=green]
            >> 2) The number of parameters in the method call is one less than[/color][/color]
            the[color=blue][color=green]
            >> number of parameters in the method declaration.[/color][/color]

            PF> No it isn't. The first argument is just in a strange place:

            PF> x.foo(y,z)
            PF> ^ ^ ^
            PF> 1 2 3

            PF> x.foo is really just weird syntax for curry(foo, x); i.e.,

            PF> q = x.foo

            PF> is essentially

            PF> q = lambda *a: foo(x, *a)

            PF> (but in a different namespace), so

            PF> x.foo(y,z) is q(y,z) is (lambda *a: foo(x, *a))(y,z)

            PF> and there are exactly as many args in the call as in the
            definition
            PF> (as, of course, there must be!)


            PF> Of course, it would be infinitely better to write foo(x,y,z),
            opening
            PF> the way to _real_ OO (with multiple dispatch), but x.foo(y,z) is
            what
            PF> Python supports...


            I was interested to read your response.
            I think you and I are looking through different lenses. Perhaps
            influenced by my Java/Smalltalk background, I'm simply uncomfortable
            when I see (1) the first parameter on all method declarations being
            the object (didn't have to do this before) and (2) the number of
            things
            between the parentheses being different for the method declaration and
            the method call.

            On the other hand, when I read your response it's clear to me that (1)
            you are comfortable looking through the lens of functional
            programming, so that passing the object in as the first parameter
            makes perfect sense to you, and (2) the mismatch in the number of
            things
            between the parentheses is not a big deal to you (though "strange",
            you admit) because as an experienced Python programmer you know what's
            going on under the covers.

            My opinions are the frank opinions of a Java/Smalltalk programmer
            learning Python. And I'm not the only newbie who thinks this seems
            strange: the original newbie poster also opined that this is "odd".

            Out of the mouths of babes ...

            Comment

            • Terry Reedy

              #36
              Re: Why the 'self' argument?


              "Jonathan Aquino" <Jon_Aquino@sha w.ca> wrote in message
              news:fc08a59c.0 309062203.56904 149@posting.goo gle.com...[color=blue]
              > , I'm simply uncomfortable when I see(2) the number of
              > things between the parentheses being different for the method
              > declaration and the method call.[/color]

              If this bothers you enough, and you have a simple class without
              inheritance, you *can* write the call in its full form
              'class.meth(ins t, a, b, ...)' so that you *do* have the same number of
              things in declaration and call. Then revert to the shorter form when
              comfortable. I am beginning to think methods calls should be taught
              this way.

              Terry J. Reedy


              Comment

              • Alex Martelli

                #37
                Re: Why the 'self' argument?

                Terry Reedy wrote:
                [color=blue]
                > "Jonathan Aquino" <Jon_Aquino@sha w.ca> wrote in message
                > news:fc08a59c.0 309062203.56904 149@posting.goo gle.com...[color=green]
                >> , I'm simply uncomfortable when I see(2) the number of
                >> things between the parentheses being different for the method
                >> declaration and the method call.[/color]
                >
                > If this bothers you enough, and you have a simple class without
                > inheritance, you *can* write the call in its full form
                > 'class.meth(ins t, a, b, ...)' so that you *do* have the same number of
                > things in declaration and call. Then revert to the shorter form when
                > comfortable. I am beginning to think methods calls should be taught
                > this way.[/color]

                The constraint of "a simple class without inheritance" is too strong.

                As long as you know the class of inst, theclass.meth(i nst, a, b) will
                work no matter how complicated theclass is or how many other
                classes it inherits from. The problem is rather one of a loss of
                polymorphism when you DON'T know the class of inst. THAT one
                you can overcome with type(inst).meth (inst, a, b) [except where
                inst is an instance of an _old-style_ class] -- now, this only leaves
                you the problem of inst having a _per-instance_ override of name
                'meth'... in that peculiar case, inst.meth would pick it up but
                type(inst).meth wouldn't.


                Alex

                Comment

                • Bengt Richter

                  #38
                  Re: Why the 'self' argument?

                  On Thu, 11 Sep 2003 11:33:38 +0000 (UTC), Grzegorz Staniak <gstaniak@inka. zagiel.pl> wrote:
                  [...][color=blue]
                  >To sum up what I've learnt here and from the FAQ: 'self' could actually be
                  >any string (it is 'self' by convention), as its role is only to serve as
                  >the first argument of methods in order to facilitate the process of
                  >associating method calls with their context. It would be possible (even if
                  >not obviously desirable) to implement method definitions in such a way
                  >as to avoid the first argument completely, i.e.
                  >
                  > def something():
                  > ...
                  >
                  >so actually it's just a question of implementation. Having little
                  >experience with object-oriented programming, I won't try to relate to
                  >pro and contra arguments that have been given in the discussion, I just
                  >have one more question: isn't it a little superfulous to have to use the
                  >'classmethod() ' builtin to declare class methods? I'd think giving a
                  >class object name as the first argument should suffice?
                  >[/color]
                  I think you may have misunderstood classmethod. Rebinding a class attribute
                  with the result of classmethod makes a method that
                  works differently from an ordinary method. It means that the class itself will
                  be bound to the first argument, even if the method is called as an attribute
                  of an instance of the class. E.g.,
                  [color=blue][color=green][color=darkred]
                  >>> class A(object):[/color][/color][/color]
                  ... def cm(*args): return args
                  ... cm = classmethod(cm)
                  ... def m(*args): return args
                  ...[color=blue][color=green][color=darkred]
                  >>> class B(A): pass[/color][/color][/color]
                  ...[color=blue][color=green][color=darkred]
                  >>> a= A()
                  >>> b= B()
                  >>> a.cm()[/color][/color][/color]
                  (<class '__main__.A'>,)[color=blue][color=green][color=darkred]
                  >>> a.m()[/color][/color][/color]
                  (<__main__.A object at 0x00902350>,)[color=blue][color=green][color=darkred]
                  >>> b.cm()[/color][/color][/color]
                  (<class '__main__.B'>,)[color=blue][color=green][color=darkred]
                  >>> b.m()[/color][/color][/color]
                  (<__main__.B object at 0x008F9690>,)

                  and note:
                  [color=blue][color=green][color=darkred]
                  >>> A.cm()[/color][/color][/color]
                  (<class '__main__.A'>,)[color=blue][color=green][color=darkred]
                  >>> B.cm()[/color][/color][/color]
                  (<class '__main__.B'>,)

                  but:[color=blue][color=green][color=darkred]
                  >>> A.m()[/color][/color][/color]
                  Traceback (most recent call last):
                  File "<stdin>", line 1, in ?
                  TypeError: unbound method m() must be called with A instance as first argument (got nothing instead)[color=blue][color=green][color=darkred]
                  >>> B.m()[/color][/color][/color]
                  Traceback (most recent call last):
                  File "<stdin>", line 1, in ?
                  TypeError: unbound method m() must be called with B instance as first argument (got nothing instead)

                  also, you don't have to do it inside the class definition, and it's not about a specific
                  attribute name -- i.e. you don't have to write xxx = classmethod(xxx ) as is conventional,
                  unless the following is fooling me.
                  [color=blue][color=green][color=darkred]
                  >>> def bar(*args): return 'bar %r'%args[/color][/color][/color]
                  ...[color=blue][color=green][color=darkred]
                  >>> class C(B): pass[/color][/color][/color]
                  ...[color=blue][color=green][color=darkred]
                  >>> C.cmbar = classmethod(bar )
                  >>> c=C()
                  >>> c.cmbar()[/color][/color][/color]
                  "bar <class '__main__.C'>"[color=blue][color=green][color=darkred]
                  >>> C.cmbar()[/color][/color][/color]
                  "bar <class '__main__.C'>"[color=blue][color=green][color=darkred]
                  >>> c.cm()[/color][/color][/color]
                  (<class '__main__.C'>,)[color=blue][color=green][color=darkred]
                  >>> c.m()[/color][/color][/color]
                  (<__main__.C object at 0x00902170>,)

                  Of course the result is not *identical*, since you can see the original function name
                  (note that you get a *bound* method either way with a classmethod, vs the ordinary C.m/c.m)
                  [color=blue][color=green][color=darkred]
                  >>> C.cmbar[/color][/color][/color]
                  <bound method type.bar of <class '__main__.C'>>[color=blue][color=green][color=darkred]
                  >>> c.cmbar[/color][/color][/color]
                  <bound method type.bar of <class '__main__.C'>>[color=blue][color=green][color=darkred]
                  >>> C.cm[/color][/color][/color]
                  <bound method type.cm of <class '__main__.C'>>[color=blue][color=green][color=darkred]
                  >>> c.cm[/color][/color][/color]
                  <bound method type.cm of <class '__main__.C'>>[color=blue][color=green][color=darkred]
                  >>> C.m[/color][/color][/color]
                  <unbound method C.m>[color=blue][color=green][color=darkred]
                  >>> c.m[/color][/color][/color]
                  <bound method C.m of <__main__.C object at 0x00902170>>

                  Regards,
                  Bengt Richter

                  Comment

                  Working...