-
-
Save bitprophet/2403699 to your computer and use it in GitHub Desktop.
>>> def init(headers={}): | |
... print "headers before: %r" % headers | |
... headers['foo'] = 'bar' | |
... print "headers after: %r" % headers | |
... | |
... | |
>>> init() | |
headers before: {} | |
headers after: {'foo': 'bar'} | |
>>> init() | |
headers before: {'foo': 'bar'} | |
headers after: {'foo': 'bar'} | |
>>> |
>>> mydict = {} | |
>>> def init(headers=mydict): | |
... print "before: %r" % headers | |
... headers['foo'] = 'bar' | |
... print "after: %r" % headers | |
... | |
... | |
>>> print mydict | |
{} | |
>>> init() | |
before: {} | |
after: {'foo': 'bar'} | |
>>> print mydict | |
{'foo': 'bar'} | |
>>> |
>>> def init(headers=None): | |
... headers = headers or {} | |
... print "before: %r" % headers | |
... headers['foo'] = 'bar' | |
... print "after: %r" % headers | |
... | |
... | |
>>> init() | |
before: {} | |
after: {'foo': 'bar'} | |
>>> init() | |
before: {} | |
after: {'foo': 'bar'} | |
>>> |
@robbyt No, because it's a mutable object (dict) and it was created at function definition time, not inside the function. (Same as with decorators, which execute at module load / function definition time and not when the function itself runs.)
Best explanation I could find quickly on Google is this SO question-and-answer (the first Edit: section explains the design decision.)
I just added a 3rd example that makes this clearer by explicitly binding the default empty-dict keyword arg value, to a real module level name. As I understand it, both 'bad' examples are 100% the same, except in the first example the dictionary is "anonymous" at module level and has no name/binding. But in both cases, Python is allocating a dictionary object in memory at module load time, and that object then gets mutated anywhere it's passed around.
The reason there's no "new" internal variable inside the scope of the function is explained (I think) in that link I gave in my previous comment.
Trivial nitpick: line 2 of good.py will change any falsey value to an empty dict. In most cases that's OK, but it's a bit safer to say if headers is None: headers={}
explicitly. It's more wordy to be sure, but people tripped up by the "non-local dict kwarg" ought to be made aware of this, too.
@donspaulding Yes, that's always a potential gotcha, though in this case I felt it was worth using for example clarity, and because the kwarg is intended to be a dict -- another type of empty value is likely to be wrong anyways, and an empty dict would evaluate to the amusing but harmless expression {} or {}
.
(You could argue that in a case where somebody gave e.g. 0
to a kwarg expecting a dict, we'd want it to ValueError
or similar instead of silently casting to empty-dict, but I'm not sure that eventuality is worth changing things up for. Maybe use headers = {} if headers is None else headers
, or just a two line `if block. A bit more verbose but also more correct.
whoa... I don't understand what's happening on line 11 of bad.py. Isn't the headers variable local to the function?