mutable default parameter problem [Prothon]
Rob Williscroft
rtw at freenet.co.uk
Sat Jun 19 08:49:25 EDT 2004
Hung Jung Lu wrote in news:8ef9bea6.0406180809.10053ba6 at posting.google.com
in comp.lang.python:
> Rob Williscroft <rtw at freenet.co.uk> wrote:
>>
>> But python has static variables.
>>
>> def another( x ):
>> y = getattr( another, 'static', 10 )
>> another.static = x
>> return y
>>
>> print another(1), another(2), another(4)
>
> What about the following case:
>
> def f():
> f.static = getattr(f, 'static', 0)
> f.static += 1
> return f.static
>
> print f(), f(), f() # prints 1 2 3
Yep this would be the way I would instinctivly write it. However
Andrea suggested:
def f():
if not hasattr( f, 'static', 0 ):
f.static = 0
f.static += 1
return f.static
Which my primitive timing tests show to be faster.
>
> As opposed to C++, you now have a line of code that is always executed
> in subsequent calls, for no good reason. This is worse than:
Well C++ has `if ( __compiler_generated_bool__ )` that will always
be executed ( except in the case of POD's (char, int, double etc) ).
>
> def f(static=[0]):
> static[0] += 1
> return static[0]
I don't know but surely the interpreter is doing some kind of
extra if/attribute lookup in there, its just faster than the
other versions.
I've timed both, and this was by far the fastest. I don't think
the problems is the `if` though. I don't know but I suspect this
version finds the local/paramiter 'static' with __slots__ like
performance, all other version's suffer from attribute lookup
problems.
>
> in the sense that you have a wasteful call ("getattr") that doesn't do
> anything productive in subsequent calls. (You could change that to
> f.static = getattr(f, 'static', 0) + 1, but the "getattr" is surely
> inefficient compared to f.static += 1, and internally likely incurs
> some conditional statement at some level.)
>
> Maybe one can do instead:
>
> def f():
> global f
> def f():
> f.static += 1
> return f.static
> f.static = 0 # initialization
> return f()
>
> print f(), f(), f() # prints 1 2 3
>
> The advantage is there is no more wasteful statements in subsequent
> calls. No "if" conditional statement.
Intresting, but it looses (in my timing tests) to the f(static=[0])
version, static[0] must be faster than f.static I guess.
> The above is of course a toy
> example to illustrate the case of a function that needs to perform
> something special the first time it is called. (I am well aware of the
> outside assignment like:
>
> def f():
> f.static += 1
> return f.static
> f.static = 0
>
> mentioned in this thread, but I am talking about something more
> general. Notice that in the latter case, f.static=0 is done before f()
> is called, which may not be what one wants. E.g.: if f() is never
> called, this assignment is wasteful. Not a problem in this case, for
> if complicated initialization is needed, like requiring a timestamp,
> it may not be a good idea.)
Indeed, the fast version is:
_f_static = 0
def f():
global _f_static
_f_static += 1
return _f_static
This is directly equivalent to
<c++>
static int f_static = 0;
int f() { return ++f_static; }
</c++>
C++ name hiding is with the static keyword, python name hiding
is with a leading underscore.
>
> In code refactoring, the equivalent is to replace conditional
> statements by polymorphism. In terms of codeblocks, what I mean is
> dynamic hook-on and hook-off of codeblocks. If the underlying language
> is powerful enough, one should be able to achieve runtime
> restructuring of code, without performance impact for subsequent
> calls.
>
Nice.
<c++>
static int f_static;
static int f_init();
static int f_run();
int (*f)() = f_init();
static int f_init()
{
f_static = /* dynamic value */ 0;
f = f_run;
return f();
}
static int f_run()
{
return ++f_static;
}
</c++>
In C++ the (performance) cost is visible and only f() and its callers
pay for it, in Python the the (performance) cost is invisible and
everybody pays for it.
In C++ I write:
int f()
{
static int var = 0;
return ++var;
}
And I let the compiler worry about the best way to implement it,
in Python I write:
class f( object ):
def __init__( self ):
self.var = 0;
def run( self ):
self.var += 1
return self.var
Though in a simple case (as all the examples have been) I
might write:
def f():
if not hasattr( f, 'static' ):
f.static = 0
f.static += 1
return f.static
Clarity (my clairty of purpose, as a programmer) wins in
both languages.
I won't be writing:
def f(static=[0]):
#etc
It simply isn't clear.
Rob.
--
http://www.victim-prime.dsl.pipex.com/
More information about the Python-list
mailing list