18
votes
cache = {}
def func():
    cache['foo'] = 'bar'
print cache['foo'] 

output

bar

Why does this work and why doesn't it require use of the global keyword?

1
global is not required for mutable objects. - Ashwini Chaudhary
@AshwiniChaudhary Care to add a reference? - akhan
@AshwiniChaudhary That's simply untrue. Try this with a list and it won't work and lists and mutable! mylist = [] def f1(): mylist = ['a']; mylist is still empty if you print it outside of f1() - samsamara
@samsamara That's not what I meant, with mylist = ['a'] you're explicitly defining a new local variable. Perform a mutation instead, like mylist.append(10) etc - Ashwini Chaudhary
Yes if you do a mutation like that, it is going to work as you are only accessing it. But say if i change my function to accept a list as a parameter, def f1(mylist): mylist = mylist + ['a']; where I'm mutating the list using the assignment operator. And I have mylist = ['s'] and I call f1(mylist). Now as lists are mutable and you passing list object to f1() yet it is still not mutating mylist. - samsamara

1 Answers

19
votes

Because you are not assigning to cache, you are changing the dictionary itself instead. cache is still pointing to the dictionary, thus is itself unchanged. The line cache['foo'] = 'bar' translates to cache.__setitem__('foo', 'bar'). In other words, the value of cache is a python dict, and that value is itself mutable.

If you tried to change what cache refers to by using cache = 'bar' instead, you would be changing what cache points to and then you need the global keyword.

Perhaps this older answer of mine to a similar question helps you understand the difference: Python list doesn't reflect variable change.