20
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?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Bunny Rabbit
  • 8,213
  • 16
  • 66
  • 106
  • 6
    `global` is not required for mutable objects. – Ashwini Chaudhary Dec 29 '12 at 11:49
  • @AshwiniChaudhary Care to add a reference? – akhan Jul 13 '18 at 04:48
  • @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 May 28 '19 at 13:03
  • @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 May 28 '19 at 15:40
  • 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 May 29 '19 at 11:35
  • So I guess the better way is to think in terms of if/how you are using the assignment operator inside a function with parameters..? – samsamara May 29 '19 at 11:44
  • @samsamara `mylist = mylist + ['a']` isn't a mutation, it results in creation of completely new list. – Ashwini Chaudhary Jun 03 '19 at 11:15

1 Answers1

25

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.

Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 2
    So this way one can defy the whole point of having the `global` keyword ? – Bunny Rabbit Dec 29 '12 at 11:51
  • 2
    @BunnyRabbit: The *point* of having the `global` keyword is for the python compiler to know what structure to change. Fully understanding why that is needed requires delving into the internal workings of Python (`locals()` versus `globals()` and byte code and such). Nothing is being defied here, a dynamic language is not about trying to prevent you from modifying globals. :-) – Martijn Pieters Dec 29 '12 at 11:53