Unexpected Python Behavior

Andrea Griffini agriff at tin.it
Tue Sep 28 02:32:37 EDT 2004


On Sun, 26 Sep 2004 10:39:15 +0200, aleaxit at yahoo.com (Alex Martelli)
wrote:

>vs your apparently implied suggestion of:
>
>def f(x):
>    if x in f.cache: ...
>f.cache = []

I like more...

   def f(x):
       if not hasattr(f,"cache"):
           f.cache = []
       ...

>which among other things suffers from f.cache having to be used in spots
>that come lexically before it's defined; or a decorator equivalent:
>
>@ set_function_attributes(cache=[])
>def f(x):
>    if x in f.cache: ...

The best I can think to is something like

    def f(x):
        static cache = []
        ...

In other languages (mostly C) there cases in which I found
nice the idea of a local name for a globally living object.

>As for 'risky', both approaches are.  The default argument risks the
>user mistakenly passing a corresponding actual-argment; the function
>attribute risks the user rebinding the name.

?

Passing a parameter to a function that, by the way, is declaring
it *wants* it doesn't seem to me the same as messing with
internals of something from the outside.

>It just don't work when the function name aint'a global, as in a
>closure; nor does it work equivalently for an overriden method, e.g.:

?

    def foo(x):
        def bar(y):
            if not hasattr(bar,"cache"):
                bar.cache = []
            bar.cache.append(x+y)
            return bar.cache
        return bar

    bar_1 = foo(1)
    bar_2 = foo(2)
    print bar_1(1),bar_1(2),bar_1(3)
    print bar_2(1),bar_2(2),bar_2(3)

>here you get two separate caches, one for base.f and one for derived.f,
>no sweat -- and if you want base.f to use derived.f's cache when call
>from there, just chance the last call to 'base.f(self, x, cache)' --
>obvious, simple, elementary.

When you need to mess with the those vars from the "outside" then
it's clear you're not looking for a static; probably you're not
even looking for a function as the interaction with the "state" is
getting too important. IMO in these cases it's *way* better to
use a class instead (may be one implementing the call interface).

>And good luck in explaining all this to beginners -- while the default
>argument approach is really trivial to explain.  Simple is better than
>complex, and a general technique like using default values for caching
>is better than a technique based on attributes which is not general
>enough to be just used everywhere.

Once reading that default were evaluated at function definition
time was enough for me; and I honestly say that I don't remember
ever falling for this trap of modifiable default values (so far).

However this seems happening quite frequently to novices; actually
*very* frequently. So either all the world is wrong or this very
specific part of the language has a problem.

>_Plus_, teaching this use of default values to beginners helps them
>understand how default values work in all cases.  Explaining the dirty
>tricks needed to extend a bit the applicability of using function
>attributes for caching offers no such nice "side advantage".

Sure there is a plus in clearly understanding that function
definition is an executable statement in python. But using
an unrelated arbitrary fact (that default values are evaluated
at that time instead than at call time) isn't IMO a good example.
A better one I would say is ...

    if raw_input("A or B ?") == "A":
        def foo(x):
            return x * 2
    else:
        def foo(x):
            return x * x

    print foo(12)

>I think there are plenty of good reasons, see above.

I didn't find any of them *really* good

>In addition, accessing a local name is of course way faster than
>accessing a global name and then an attribute thereof.

Yeah, static are slower than necessary; and uglier also.

May be the direction could be fixing that instead of just
pushing towards an ugly hack that just happens to work.

>Yes, it helps them a lot to understand, realize, and remember, that
>default values are shared among all of a function's calls.

That's the wart!
 
>It's simpler and more general.

To me seems an unrelated side-effect of the decision of
when to evaluate default parameters. I'm not questioning
the decision per se (it has pros and cons... for example
you can't use things like "def foo(x,y,z=x+y)") but that
using fake parameters for static is ugly and error prone.


Just my 0.02 euros of (still) fresh eye opinion


Andrea



More information about the Python-list mailing list