Why the 'self' argument?

  • Thread starter Grzegorz Staniak
  • Start date
A

Alex Martelli

Grant Edwards wrote:
...
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.

Java and Ruby have no "normal functions" -- just methods. Ruby
has what may LOOK like ordinary functions, but they're methods
of the module object...


Alex
 
D

Dave Brueck

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,

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 SomeEventGenerator(eventListener):
eventListener(5, 6)

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

f = Foo()
SomeEventGenerator(foo_OnEvent)
SomeEventGenerator(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
 
M

Michael Peuser

John Roth said:
You could still give them different names. The proposal is to eliminate
the need to specify "self" in the method header, not in the code in the
method body. That's a different issue, and I don't know of any language
that manages to do it consistently.

You'll run into all the problems known from the Pascal with-statement. And
that's not only a readability matter.

Kindly
Michael P
 
I

Isaac To

John> You could still give them different names. The proposal is to
John> eliminate the need to specify "self" in the method header, not in
John> the code in the method body. That's a different issue, and I don't
John> know of any language that manages to do it consistently.

Michael> You'll run into all the problems known from the Pascal
Michael> with-statement. And that's not only a readability matter.

I think John is talking about a syntax that have the self formal argument
implicit, but the self actual argument explicit. Something like this:

class foo:
def __init__(msg):
self.msg = str(msg)
def f(arg):
print self.msg + ', ' + str(arg)
v = foo('hello')
v.f('world') # equivalent to foo.f(v, 'world')

which would be equivalent to the current Python

class foo:
def __init__(self, msg):
self.msg = str(msg)
def f(self, arg):
print self.msg + ', ' + str(arg)
v = foo('hello')
v.f('world') # equivalent to foo.f(v, 'world')

If that's really what he mean, he is correct in every word: the only change
is that you treat self as a reserved word, and the real road-block is that
everybody think that self is not reserved. There is a problem that you can
no longer define normal function in a class, though. The interpretor would
think that the function is a method.

Regards,
Isaac.
 
B

Bengt Richter

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."


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?
('howdy',)
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)
('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).

(The above doesn't even get into the multiple name issues I alluded to).

Regards,
Bengt Richter
 
J

Jonathan Aquino

Two reasons I cringe when I see self as the first parameter of method
declarations (i.e. "def methodname(self, ...)" (I don't mind self in
the method body)):

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

2) The number of parameters in the method call is one less than the
number of parameters in the method declaration. I'm annoyed when I see
this because I want the rules of the language to be obvious (in this
case, a one-to-one mapping). I would be embarassed to have to explain
this to a beginning programmer.

(I do like how Python indenting indicates blocks, however)
 
H

Harri Pesonen

Jonathan said:
Two reasons I cringe when I see self as the first parameter of method
declarations (i.e. "def methodname(self, ...)" (I don't mind self in
the method body)):

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

2) The number of parameters in the method call is one less than the
number of parameters in the method declaration. I'm annoyed when I see
this because I want the rules of the language to be obvious (in this
case, a one-to-one mapping). I would be embarassed to have to explain
this to a beginning programmer.

(I do like how Python indenting indicates blocks, however)

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, it
feels like the whole class feature was added later. The Quick Python
book says that Python was designed to be object oriented from the ground
up. Is it true?

Harri
 
G

Grant Edwards

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,

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.
 
J

John Roth

Bengt Richter said:
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."


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?
('howdy',)
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.)

John Roth
 
J

John Roth

Harri Pesonen said:
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, it
feels like the whole class feature was added later. The Quick Python
book says that Python was designed to be object oriented from the ground
up. Is it true?

I'm not sure; someone who knows the history back to the beginning
would have to say. However, there's a difference between "from the
ground up," which is certainly true, and "from the beginning" which
may or may not be true.

John Roth
 
T

Terry Reedy

Isaac To said:
class foo:
def __init__(msg):
self.msg = str(msg)
def f(arg):
print self.msg + ', ' + str(arg)
v = foo('hello')
v.f('world') # equivalent to foo.f(v, 'world')

I think this equivalence, being an answer to the subject sentence,
needs more emphasis. An instance method is a wrapped function and a
*class* attribute. The 'proper' way to call such functions,
consistent with all other function calls, is klass.meth(inst,
*rest_of_args), with inst as an explicit first arg matching the
explicit first parameter in the definition. To me, having an explicit
arg match an implicit param would be a warty inconsistency.

From this viewpoint, inst.meth(*rest_of_args) is a conveinient
abbreviation that works because of the attribute inheritance and
lookup mechanism. Of course, beyond being just syntactic sugar, it
adds the very important flexibility of the programmer not having to
know precisely which base class will provide the method. I think the
long form of method call should be taught first, and then the
abbreviation and behavioral reason for its existence. (Perhaps then,
we would have fewer threads on this topic ;-)

If an ad hoc 'self'-rule were added, the inconsistency would have to
be consistently applied to all function definitions. Python attaches
little special importance to the presence of a def within or without a
class suite.
Absent a metaclass override that affects function wrapping only for
those in the .__new__() dict arg but not when later assigned,

class k(object): # or no object
def f(self): pass

nicely abbreviates

def f(self): pass
class k(object): pass
k.f = f
del f

Terry J. Reedy
 
T

Terry Reedy

Harri Pesonen said:
I agree, it's not logical.

Do my previous two responses today make the logic any clearer?
I'm learning Python at the moment, and like it very much.

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
 
H

Harri Pesonen

Grant said:
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.


Why?

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
 
B

Bengt Richter

Bengt Richter said:
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')

('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:
... 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
... This uses
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:
x=2, y='3', x*y='33'
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 ;-/

Regards,
Bengt Richter
 
J

Jonathan Aquino

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


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, ...

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 ...
 
T

Terry Reedy

Jonathan Aquino said:
, 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.

If this bothers you enough, and you have a simple class without
inheritance, you *can* write the call in its full form
'class.meth(inst, 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
 
A

Alex Martelli

Terry said:
If this bothers you enough, and you have a simple class without
inheritance, you *can* write the call in its full form
'class.meth(inst, 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.

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

As long as you know the class of inst, theclass.meth(inst, 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
 
N

Neil Padgen

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.

Is this what you mean?
.... __slots__ = ['a', 'b', 'c']
....Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'A' object has no attribute 'd'

-- Neil

--
 
J

John Roth

Harri Pesonen said:
Grant said:
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.

I believe that is what __slots__ is for. That's run time rather than
compile time, but at least it gives you the error on the statement
that attempts to do an invalid assignment.
Also if
variable declarations could have the type, again some errors could be
detected at compile time.

It's quite possible to add run time type checking to Python, at a
significant cost in performance. See the descriptors material.

There's been quite a bit of work on a compile time type checking
system over the years, but it has never resulted in a proposal that could
be agreed on.

My own personal opinion is that I like the type check system in
the ML family of languages: it stays out of your way unless you need it.
How that would fit in Python is another question, though.
At this time, it looks like an issue for Python 3.0, which seems to
keep receeding into the distance.

John Roth
 
H

Harri Pesonen

John said:
Grant said:
I believe that is what __slots__ is for. That's run time rather than
compile time, but at least it gives you the error on the statement
that attempts to do an invalid assignment.

Thanks guys, I hadn't heard of __slots__ before. Perhaps I am reading a
too old book. Of course, a compile time check would be better...
It's quite possible to add run time type checking to Python, at a
significant cost in performance. See the descriptors material.

I think that Python already has run time type checking. It does not
allow "a" + 1, for example.
There's been quite a bit of work on a compile time type checking
system over the years, but it has never resulted in a proposal that could
be agreed on.

It would be quite simple to have "option explicit" like in Visual Basic,
I believe. Like the following:

option explicit
var a = "asdf"
b = 1 # error at compile time
a = 123
(var b, var c, var d) = (1, 2, 3)
e = "asdf" # error at compile time

Always have "var" keyword before first assignment. Just a proposal. You
don't need __slots__ (which sounds like a hack):

class A(object):
var a, var b, var c
# instead of __slots__ = ['a', 'b', 'c']

foo = A()
foo.a = 1
foo.b = 2
foo.c = 3
foo.d = 4 # error at compile time
My own personal opinion is that I like the type check system in
the ML family of languages: it stays out of your way unless you need it.
How that would fit in Python is another question, though.
At this time, it looks like an issue for Python 3.0, which seems to
keep receeding into the distance.

I think that the type checking is not so important, but you could have
it as well:

var a as str = "asdf"
var b as int = 123
var c as float = 123.4
a = 123 # error at compile time
# also at run time if not detected earlier
var d = "asdf"
d = 123 # ok, because no type was defined

Harri
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top