Bug in python!? persistent value of an optional parameter in function!

C

C Barr Leigh

Help! Have I found a serious bug?
This seems like highly undesired behaviour to me. From the program
below, I get output:

call1: ['sdf']
call2: ['Set within test for call2']
call3: ['Set within test for call2']

instead of what I should get,

call1: ['sdf']
call2: ['Set within test for call2']
call3: ['Set within test for call3']

I'm using Python 2.4.4c1 (#2, Oct 11 2006, 21:51:02). The code is
below.


#!/usr/bin/python

def testPersistence(anarg,twooption=[]):
#print anarg
if not twooption:
twooption.append('Set within test for '+anarg)
print anarg +': '+str(twooption)

testPersistence('call1',twooption=['sdf']);
testPersistence('call2');
testPersistence('call3');
 
P

Paul Rubin

C Barr Leigh said:
Help! Have I found a serious bug?
This seems like highly undesired behaviour to me. From the program
below, I get output:

It is intentional, not a bug, see the docs. Whether it's desirable is
a different question.
 
J

John Nagle

Paul said:
It is intentional, not a bug, see the docs. Whether it's desirable is
a different question.

True. It would make sense to disallow mutable values as
initial values for optional arguments. The present behavior
is silly.

John Nagle
 
P

Paul Rubin

John Nagle said:
True. It would make sense to disallow mutable values as
initial values for optional arguments. The present behavior is silly.

That would be the worst of both worlds. The main alternative to the
present behavior is re-computing the default value every time the
function is entered.
 
G

George Sakkis

True. It would make sense to disallow mutable values as
initial values for optional arguments. The present behavior
is silly.

1. If you'd given it a little more thought than it took you to write
this, you would perhaps realize that it is in general impossible to
determine automatically whether an arbitrary object is mutable or
not.

2. Even if you could determine it, it would be overly restrictive to
disallow all mutable values from defaults. The fact that an object is
mutable doesn't mean that the function will try to mutate it:

def paintWall(ind, colormap={1:'red', 2:'blue', 5:'green'}):
print "Let's paint the wall %s" % colormap[ind]


George
 
S

Steven D'Aprano

True. It would make sense to disallow mutable values as
initial values for optional arguments. The present behavior
is silly.

It's just *different*, not silly.

It's also quite useful in some circumstances, e.g. cheap caching without
using a global variable.

def factorial(n, _cache={}):
try:
return _cache[n]
except KeyError:
# fall back to slow calculation
if n <= 1:
result = 1
else:
result = factorial(n-1)*n
_cache[n] = result
return result

There are other ways of implementing caches, but this is quick and easy
and works well for many functions.
 
P

Paul Rubin

Steven D'Aprano said:
def factorial(n, _cache={}):
try:
return _cache[n]
except KeyError:
There are other ways of implementing caches, but this is quick and easy
and works well for many functions.

I like this better (examples are untested):

def factorial(n):
try:
return factorial.cache[n]
except KeyError: pass
...
factorial.cache = {}

you could even use a decorator:

def memoize(f): # memoize a 1-arg function
f.cache = {}
def g(f,x):
if x in f.cache: return f.cache[x]
return f.cache.setdefault(x, f(x))
return functools.partial(g,f)
...

@memoize
def factorial(n):
return 1 if n==0 else n*factorial(n-1)
 
C

C Barr Leigh

Oh, oops! Of course... :) A great and sensible feature if you're
expecting it.
Thanks very much, everyone, for the links and discussion!

Chris
 
B

Bjoern Schliessmann

John said:
True. It would make sense to disallow mutable values as
initial values for optional arguments. The present behavior
is silly.

Why? You're free to only use immutables.

Regards,


Björn
 
B

Bruno Desthuilliers

C Barr Leigh a écrit :
Help! Have I found a serious bug?

No. This is a FAQ. Default arguments of functions are evaled only once -
when the def statement is eval'd and the function object constructed.
This seems like highly undesired behaviour to me.

Possibly, but this is unlikely to change in a near future, so you'd
better get used to it. If you want mutable objects as default values for
functions, the idiom is:

def some_func(default=None):
if default is None:
default = []
# code here

HTH
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top