Re: extending a class without subclassing

Guido.van.Rossum@cwi.nl
Fri, 20 Jan 1995 15:10:33 +0100

(This is a reply to a private email from Graham but my reply is
intended for a larger audience.)

Me:
> >Yes. If C is an existing class, and f is a *function*, you can f to C
> >(permanently) by assigning f to C.f. For example:
> >
> > def additional_method(self):
> > pass # whatever...
> >
> > from Tkinter import Text
> > Text.additional_method = additional_method
You:
> The problem with this is that if I put all these extensions in a file (like
> I would do if they were a subclass), then to import the extensions I can't
> just import some class name. I have to exec a file - which is non orthogonal
> to the normal way (when you are programming for mathematicians to use your
> programmers keeping everything simple is a huge consideration :-)!).

OK. Here's an alternative using multiple inheritance. If I remember
correctly, what you want is to add the same set of extensions to both
the Text and the ScrolledText widgets.

Define a private "mix-in" class which contains just the new methods.
For example:

class _MyMixinClass:
def myxinmethod1(...): ...
...

Then define two public classes that derive both from your mix-in class
and from the library classes. These define no methods themselves:

class MyText(_MyMixinClass, Text): pass

class MyScrolledText(_MyMixinClass, ScrolledText): pass

Then your users can import MyText and MyScrolledText. The advantage
over patching up the library classes is also that code that expects
the standard library behavior isn't affected.

On to other Python semantic details:

> Again I am not sure that this is exactly what I want. Is the
> following legal python,
>
> class P(C):
>
> def f(self):
> ...
>
> C.f = P.__dict__['f']

This won't work exactly as written here, since *inside* the class
definition, the name "P" is not yet defined. What would work however
is this, and much simpler it is, too:

C.f = f

---- Sidebar: ----

It helps to understand Python's model of class and function
definitions here: in Python, *every* statement has to be executed in
order to be effective. ("global" is the only exception.)

The execution of a function definition, say

def f(arg):
...body...

creates a function object containing the executable representation of
the function body and a pointer to the current global name space, and
assigns it to the local identifier 'f'.

The execution of a class definition, say

class C:
def meth1(self, args): ...
def meth2(self, args): ...
...

is executed quite differently: a temporary local environment is
created, and the statements *inside* the class definition (i.e.
"def meth1...", "def meth2..." etc.) are executed. This creates local
variables meth1, meth2, and so on. At the end of the class definition
statement, the contents of the temporary local environment is wrapped
up in a class object which is then assigned to the variable C -- in
the original local environment.

Note that there is no restriction that a class definition can only
contain method definitions: it can assignments, too, and in fact
anything else -- though usually it's not very useful. Assignments can
be used to create aliases for methods, however, for example:

class C:
def method_with_a_really_long_and_descriptive_name(self):
self.do_something_really_complicated()
shorthand = method_with_a_really_long_and_descriptive_name

Now, if x is an instance of C, x.shorthand() will have the same effect
as x.method_with_a_really_long_and_descriptive_name().

---- end sidebar ----

> ie. an assigment statement, rather than a def method, inside a class
> (but not inside a method in that class). If it is legal python how
> often will the assignment statement get executed? Every time I
> import the file containing P, or just on the first import?

Yes this is legal. As I tried to explain in the sidebar above, the
assignment is only executed once for each time the class definition is
executed -- and the class definition is only executed once per program
execution, no matter how many times the module containing it is
imported (this is standard import semantics in Python -- only the
first import of a particular module executes the code in the module).

> What I am trying to do is to make extension classes seem to be just
> like normal classes (imported the same way, behave the same,
> etc). To do this I need to be able to put my new methods into a
> class, and have them added to the class I am extending exactly once.

This will work alright -- but I think the solution using multiple
inheritance is nicer.

> While I have you here (heard that before? :-)), is there any way to declare
> methods or instance variables private to a class. I write a lot of small
> functions inside my classes and would like to make some of them private
> since they are really just class level utilities.

The official answer to this question is "not through language
mechanisms". I advise to use a naming convention -- I often prefix
private methods with a single underscore. (Don't use the "__spam__"
convention -- that's reserved for defining names that have a special
meaning to the interpreter, e.g. __init__ and __add__.)

--Guido van Rossum, CWI, Amsterdam <mailto:Guido.van.Rossum@cwi.nl>
<http://www.cwi.nl/cwi/people/Guido.van.Rossum.html>