As a new Python programmer I was surprised by the behavior of default parameter expressions. I knew they were only executed once when the function is first defined, but I hadn't realized the ramifications for using a static dictionary object as a default expression. This little gotcha hit me yesterday and took quite a while for me to figure out what was happening. Here's some sample code:
def Bad(dict={}): print "Bad: %s" % repr(dict) dict['p'] = 1 Bad() Bad()
Which results in:
Bad: {} Bad: {'p': 1}
Since the value of dict is mutable, it can be changed as a side effect of the function. So all subsequent calls will use a default value that has been modified by previous calls to the function! This can be (and was) a nightmare to track down in a large program.
I fixed this by changing to the following:
def Good(dict=None): if dict == None: dict = {} print "Good: %s" % repr(dict) dict['p'] = 1 Good() Good()
Which results in:
Good: {} Good: {}
The fix de-couples the effects of one call on subsequent calls (as was the intention of the original code).
It's usually not a good idea to use built-in function names liked dict as variable names. When checking to see if an object is None, use object identity comparison instead of ==. Use %r to print repr.
ReplyDeletedef Good(arg=None):
if arg is None:
arg = {}
print "Good: %r" % dict
arg["p"] = 1
Thanks! I never use "dict" explicitly, so I forgot that was a Python built-in class - oops!
ReplyDeleteI also like your other two tips; thanks for the lesson!
Dict is the most wonderful thing. This
ReplyDeletem = dict(a=1, b=2)
is easier to read than:
m = {'a': 1, 'b': 2}