[Tutor] multiple class instances

Tim Peters tim_one@email.msn.com
Mon, 10 May 1999 23:19:26 -0400


[Corran Webster, explaining the unexplainable!]
> ...
> You don't really need to understand what's going on here to avoid
> problems, rather you just need to remember the rule:
>
> "Avoid mutable default values in functions and methods"
>
> I can't think of a situation where this sort of default argument
> behaviour would be useful, although I'm sure Tim Peters could come up
> with one <wink>.

Everything that isn't forbidden becomes vital to someone <0.5 wink>.

One reasonable use is to supply a default for a sequence argument that
*isn't* (and never will be) mutated:

class Set:
    def __init__(self, initial=[]):
        """Set([initial]) -> construct a Set.

        By default, the Set is empty.  If optional arg initial is
        given, it must be a sequence, and the Set is initialized to
        contain (only) its elements.
        """

        self.dict = dict = {}
        for element in initial:
            dict[element] = 1    # value irrelevant

Perfectly safe!  Because "initial" is neither changed nor saved away; it's
merely examined once and then forgotten.

So we can refine your rule to:

    "Avoid mutable default values in functions and methods,
     unless they're not and never can be mutated"

If the latter is true, they may as well be immutable!

The other good use violates even that rule, but in this case the user is
never supposed to supply a value:

def fac(n, _cache={}):
    """fac(n) -> factorial of n; n must be >= 0."""
    try:
        return _cache[n]
    except KeyError:
        if n < 0:
            raise ValueError("fac argument must be >= 0: " + `n`)
        if n <= 1:
            result = 1L
        else:
            result = n * fac(n - 1)
        _cache[n] = result
        return result

This is a common trick for speeding recursive functions, using _cache to
remember the results that have already been computed.  You could do this
with a global var _cache too, but this way hides the name and-- by making
the name local instead of global --happens to slash the time it takes Python
to "look the name up".  OTOH, if someone calls fac with *two* arguments, it
will screw up the function beyond repair.

Fair summary:  Avoid mutable default values in functions and methods <wink>.

unless-you-have-a-cool-reason-to-tempt-fate-ly y'rs  - tim