static methods

Alex Martelli aleaxit at yahoo.com
Mon Mar 26 05:52:52 EST 2001


"Christian Tanzer" <tanzer at swing.co.at> wrote in message
news:mailman.985586915.11526.python-list at python.org...
    [snip]
> > Otherwise, you have to pass both the class reference and the module
> > reference to the caller.
>
> Actually, the module reference will suffice; it will contain suitably
> named callables to:
>     -- generate instances of the class (the class-object itself and/or
>         suitable factory functions),
>     -- do whatever else you need to be done class-wise.
>
> You can package up those callables any way you want; there is
> really no special reason they should be attributes of the class
> object as opposed to that of any other object, and a module
> object is typically handiest.

: This seems to be the standard answer on c.l.py. I'm not convinced.
: There are two cases:
: - Modules defining a single class.
:  In this case, most people would use the same name for both module
:  and class. If you need the module object for scoping purposes, you
:  end up with code using lots of `Bletch.Bletch' which I find atrocious.

You don't often need to obtain *the class-object* from your caller --
it's generally only needed when you're defining a class of yours
*that _inherits_ from a caller-supplied class-object*; a pretty rare,
although undoubtedly elegant, highly-dynamic idiom.  For those pretty
rare needs, _when you also further need a few other functions that
are not-instance specific_ (also caller-supplied ones), I find that
just about any way to package things up works -- sure, you and your
caller may need to agree on attribute-names or whatnot, unless you
want to pass several objects, but then, you'd anyway need to agree on
the N names of the various non-instance-specific functions' names, and
one more agreed-upon attribute name (for the class-object itself), for
a total of N+1 instead of N, is hardly a hardship such as to justify
language-changes!

More often, what you need to get from your caller is just *a bunch
of callables* -- one or more to generate objects of the appropriate
class[es], zero or more to operate 'generically' (not as methods of
some instance).  Here, the class object _might_ be one of the
callables in the first subset (factory-callables), but there is
really no advantage in singling it out for special treatment as
against other factory-callables, and why should the *other* (if
any) generic functions be preferably packaged up as attributes
of one of the factory-callables?!  It seems a peculiar arrangement
indeed.  I see nothing wrong with needing a trivial wrapper to
setup this packaging arrangement if you're really keen on it.
Note that, in any case, you'll STILL need naming-conventions for
(at best) all but one of the callables (unless you want to pass
them as separate objects, of course).

The handiest packaging helper is the usual suspect:

class ABunchOfStuff:
    def __init__(self, **kwds):
        self.__dict__.merge(kwds)

whenever you need to package up, and pass as a function
argument, a bunch of stuff with some set of agreed names,
you do so by passing a suitably initialized ABunchOfStuff
instance.  There are no restrictions to the kinds of
callables (or other objects) that can live as attributes
of this bunch-o'-stuff, nor on their names.


:  Rick also talked about passing around a reference to the class. If
:  you pass a reference to a module instead, you need a convention how
:  to refer to the class defined by the module.

If you don't need other callables, but just one factory callable,
it's fine to pass just that callable object, of course -- it may,
as a special case, be the class-object, just as of-course.  When
you DO need to pass N+M callables (M of them being factories, the
other N non-factory, non-instance-directed generics) you do need
to pass them separately, or to have some naming convention if you
want them to pass them as attributes of some object, or other kind
of similar conventions (e.g. you could pass one or two tuples or
other sequences, but then you'll need conventions on the order in
which various callables occupy item slots in those sequences --
quite analogous to a naming-convention:-).

There's nothing intrinsically superior about the packaging
convention of passing one object and having the other N+M-1
be attributes of that distinguished object (and that still
needs a naming convention anyway).  You can get that sort of
arrangement if you want, but I don't see why a language should
go out of its way to make it handier for you wrt other kinds
of arrangements.

: Modules defining multiple classes. In this case, passing a module
: reference instead of a class reference won't fly, anyway.

It's still an issue of how you arrange/package things up,
naming (or other organizational) conventions, etc.  If I
have multiple similar classes in a module, I'm _particularly_
likely to want to pass from it, as the factory callable,
something OTHER than one of those class-objects -- a
factory function that will choose _which_ exact class to
instantiate depending on arguments, environment, etc.  If
the other generic functions need to know what specific _class_
has been instantiated, i.e. they're not really as generic
as all that, then having them as attributes of the actual
_instance_ (not necessarily the _class_ thereof) may work.

The module-object may be the appropriate object to pass if
you rely on naming conventions for the factory function[s]
and semi-generics.  Alternatively, repackaging whatever
appropriate subset of callables into a BunchOStuff instance
works just fine; as do other somewhat more complex approaches,
but I tend to prefer simplicity whenever feasible.

: You could dismiss modules with multiple classes out of hand, but
: IMNSHO this would be rash (and inconsistent with your approval of
: Lakos' ideas about large scale design).

Why should modules with multiple classes be problematic
here?  A given factory-function may instantiate the
proper class, whether the candidate classes are all in
one module, or spread among several modules, just as
easily.

> The desire to use the class-object specifically, rather than any
> other, typically seems to have more to do with familiarity with
> other languages where classes must play certain special roles
> (C++, Java, Smalltalk, Eiffel) than with any actual programming
> needs.

: I used languages with strong emphasis on the module as primary
: construct (like Modula-2 and Ada) and languages favoring the
: class as the primary construct (like Eiffel and C++). I found the
: latter much better suited to my programming needs. That doesn't mean

But does Modula-2 (or the Ada version you used) offer classes
*at all*?  Modules per se don't offer enough _polymorphism_ (if
any), which is a rather different issue; classes (or at-least
equivalent constructs, such as 'prototype objects' in languages
oriented to their use) are a real need because of that, while
not necessarily usurping modules' role for subsystem packaging.

: that I shun modules, though. Fortunately, Python doesn't force either
: direction (with some tricks to get class functions).

The well-known tricks are one possibility, sure, but different
organizations of the needed callables often shine brighter.


Alex







More information about the Python-list mailing list