Re: Some suggestion for python classes

Guido.van.Rossum@cwi.nl
Mon, 10 May 1993 21:17:09 +0200

Jaap Vermeulen writes:

> 1) When using a class method, it would be nice if the calling class
> would be added as the 1st argument of the method.
>
> First let me explain the nomenclature. A class method is a method
> invoked through the class instead of the method. I.e.
>
> class foo:
> def bar():
> something
>
> Invoking foo.bar() is invoking a class method. You could invoke
> the same method as a instance method, foo().bar(), however, it
> would result in a 'TypeError: arg cound mismatch' since the
> instance itself is automatically prepended to the argument list.
> What I propose is to do the same when you invoke the method as a
> class method, and prepend the class to the method. This would
> result in:
>
> class foo:
> def bar(self):
> print type(self)
>
> Invoking foo.bar() would result in <type 'class'>, invoking
> foo().bar() would result in <type 'instance'>. It is up to the
> programmer to correctly code, invoke and identify class method vs.
> instance methods (as with a lot of other things in python).
>
> The benefit would be that you can define a class method in a base
> class, and still have access to the invoking class. i.e.
>
> class foo:
> def new(self):
> new_instance = self()
> ...
> return new_instance
>
> class bar(foo):
> ...
>
> Invoking bar.new() would return an instance of bar. The current
> work-around is to invoke the class method as bar.new(bar).

Ah, but calling a class method is also used in another situation. The
standard "idiom" for extending a method is as follows:

class Base:
def doit(self, how):
"do it in a basic way"

class Derived(Base):
def doit(self, how):
"do a little bit in advance"
Base.doit(self, how)
"do it some more"

For the interpreter, the call to Base.doit(self, how) in a method of a
Derived instance is indistinguishable from a call somewhere else.
This is actually one of the foundations of Python's "cheap"
implementation of classes and inheritances, and I would hate to lose
it. (The point is that the interpreter has no idea that it is
executing a method -- all it ever sees is function calls.)

I have a feeling that the only situation where a class method needs to
know the class from which it was really called is in situations where
you want to create a new class instance without any reference to
another. There is already a standard way to do this, and I don't see
why "C.new()" is much better than "C().init()" -- the two extra
parentheses don't really bother me. If you insist on having a
function called new() to create class instances, why not make it a
global function and call it with the class as explicit argument, e.g.
"new(C)" ?

But maybe your example is too simplistic to show what the real problem
is. What does your new() function do between creating the new
instance and returning it? Can't this be done by the instance's
init() function?

> 2) It would be nice to have a special operator to implement the
> 'super' functionality. This functionality allows you to start
> searching for a method starting at the base classes, not the
> current class. The current workaround is to define a function to
> delete the method from the current class (if it's there), retrieve
> it using the normal method, and replacing the method in the current
> class. This allows you to write:
>
> class bar(foo):
> def new(self):
> new_instance = super(self, 'new')(self)
> ...
> return new_instance
>
> What I would like to see is some special operator that does this
> for you, e.g. using a carrot:
>
> new_instance = self^new()

See my example above of how to extend a method in Python. The point
is that the name of the base class is statically known at the point
where you write the code that needs to use it, so there really is no
good reason to dynamically look it up -- you might as well use the
name of the base class.

You don't give an implementation of your super() workaround -- I have
a feeling that it might fail if there are several layers of derived
classes stacked on top of bar and some of them don't define a new()
method.

(BTW, I always thought that '^' was called "caret" -- is this a
special version with night vision? :-)

> 3) It would be nice to be able to retrieve the name of a class. The
> attribute exists, but is empty. I.e. bar.__name__ returns None.
> Also, the address operator Guido was talking about would come in
> very handy, since you would be able to define something like this
> (given that '&' is the address operator):
>
> class bar(foo):
> def __repr__(self):
> try: return '<class ' + self.__name__ + 'at ' + `&self`
> except:
> return '<instance ' + self.__class__.__name__ + 'at ' + `&self`

Yes, this is a bug. I should fix it. It's difficult to fix because
of the way class construction is implemented, but nevertheless I
should fix it. BTW, the address-of operator will be called id() and
return a plain integer. (Adding unary & would mean a change to the
grammar, and that's much too drastic for such a little-used function.)

> This brings up another point, and that is that __repr__ currently
> doesn't work for classes, it would be nice if it did.

I don't understand how you want it to work. Do you mean that a class
should be able to decide how it will be printed? How would you use
that? How would you distinguish between the __repr__ method for
instances and the one for the class?

General comments: I have a feeling that you are trying to use classes
where you should be using instances; this would also explain a few of
your wishes above. But again, maybe I'm missing your point -- please
give more realistic examples to show how you are using all this.

Your ideas are certainly worth being investigated, it's just that
everything fits together so tightly that moving one piece usually
requires moving LOTS of other pieces. Sometimes at first you don't
see why a piece is necessary, until you take several steps backwards
and see the whole structure again...

Cheers,

--Guido van Rossum, CWI, Amsterdam <Guido.van.Rossum@cwi.nl>