tags:

views:

53

answers:

2
import inspect
class Test:
  def test(self, p, d={}):
    d.update(p)
    return d
print inspect.getargspec(getattr(Test, 'test'))[3]
print Test().test({'1':True})
print inspect.getargspec(getattr(Test, 'test'))[3]

I would expect the argspec for Test.test not to change but because of dict.update it does. Why?

+5  A: 

Because dicts are mutable objects. When you call d.update(p), you are actually mutating the default instance of the dict. This is a common catch; in particular, you should never use a mutable object as a default value in the list of arguments.

A better way to do this is as follows:

class Test:
    def test(self, p, d = None):
        if d is None:
            d = {}
        d.update(p)
        return d
Tamás
`d = d or {}` is sort of odd in that if someone passes in *their own* dict object with stuff you mutate it but if someone passes in *their own* dict object (or another kind of object that works sort of like it) that is empty, it uses a new dict object instead. If I was writing code that did something like this, I'd probably use `if d is None: d = {}` or, more likely, never mutate the argument I receive to start with.
Mike Graham
You got a point here - I'm editing my solution to accomodate that. Anyway, I also agree that one should never mutate the argument that was received there.
Tamás
+2  A: 

A default argument in Python is whatever object was set when the function was defined, even if you set a mutable object. This question should explain what that means and why Python is the SO question least astonishment in python: the mutable default argument.

Basically, the same default object is used every time the function is called, rather than a new copy being made each time. For example:

>>> def f(xs=[]):
...   xs.append(5)
...   print xs
... 
>>> f()
[5]
>>> f()
[5, 5]

The easiest way around this is to make your actual default argument None, and then simply check for None and provide a default in the function, for example:

>>> def f(xs=None):
...   if xs is None:
...     xs = []
...   xs.append(5)
...   print xs
... 
>>> f()
[5]
>>> f()
[5]
Eli Courtwright
Note that "Default arguments in Python are mutable." isn't entirely true. Default arguments can be mutable (when they're mutable objects, like lists) and immutable (when they're immutable, like ints). The part that is somewhat important is that only one object is set as the default argument.
Mike Graham
@Mike: Good point, I've edited my answer to improve the wording of that statement.
Eli Courtwright