Global dictionaries don't need keyword global to modify them?

81,741

Solution 1

The reason is that the line

stringvar = "bar"

is ambiguous, it could be referring to a global variable, or it could be creating a new local variable called stringvar. In this case, Python defaults to assuming it is a local variable unless the global keyword has already been used.

However, the line

dictvar['key1'] += 1

Is entirely unambiguous. It can be referring only to the global variable dictvar, since dictvar must already exist for the statement not to throw an error.

This is not specific to dictionaries- the same is true for lists:

listvar = ["hello", "world"]

def listfoo():
    listvar[0] = "goodbye"

or other kinds of objects:

class MyClass:
    foo = 1
myclassvar = MyClass()

def myclassfoo():
    myclassvar.foo = 2

It's true whenever a mutating operation is used rather than a rebinding one.

Solution 2

You can modify any mutable object without using global keyword.

This is possible in Python because global is used when you want to reassign new objects to variable names already used in global scope or to define new global variables.

But in case of mutable objects you're not re-assigning anything, you're just modifying them in-place, therefore Python simply loads them from global scope and modifies them.

As docs say:

It would be impossible to assign to a global variable without global.

In [101]: dic = {}

In [102]: lis = []

In [103]: def func():
    dic['a'] = 'foo'
    lis.append('foo') # but  fails for lis += ['something']
   .....:     

In [104]: func()

In [105]: dic, lis
Out[105]: ({'a': 'foo'}, ['foo'])

dis.dis:

In [121]: dis.dis(func)
  2           0 LOAD_CONST               1 ('foo')
              3 LOAD_GLOBAL              0 (dic)     # the global object dic is loaded
              6 LOAD_CONST               2 ('a')
              9 STORE_SUBSCR                         # modify the same object

  3          10 LOAD_GLOBAL              1 (lis)    # the global object lis is loaded
             13 LOAD_ATTR                2 (append)
             16 LOAD_CONST               1 ('foo')
             19 CALL_FUNCTION            1
             22 POP_TOP             
             23 LOAD_CONST               0 (None)
             26 RETURN_VALUE  
Share:
81,741
Jovik
Author by

Jovik

Updated on July 09, 2022

Comments

  • Jovik
    Jovik almost 2 years

    I wonder why I can change global dictionary without global keyword? Why it's mandatory for other types? Is there any logic behind this?

    E.g. code:

    #!/usr/bin/env python3
    
    stringvar = "mod"
    dictvar = {'key1': 1,
               'key2': 2}
    
    def foo():
        dictvar['key1'] += 1
    
    def bar():
        stringvar = "bar"
        print(stringvar)
    
    print(dictvar)
    foo()
    print(dictvar)
    
    print(stringvar)
    bar()
    print(stringvar)
    

    Gives following results:

    me@pc:~/$ ./globalDict.py 
    {'key2': 2, 'key1': 1}
    {'key2': 2, 'key1': 2}  # Dictionary value has been changed
    mod
    bar
    mod
    

    where I would expect:

    me@pc:~/$ ./globalDict.py 
    {'key2': 2, 'key1': 1}
    {'key2': 2, 'key1': 1}  # I didn't use global, so dictionary remains the same
    mod
    bar
    mod
    
    • Jovik
      Jovik over 11 years
      I read SO: Global keyword in Python and SO: Why is keyword global not required, so I know how dictionary works without global, but I still fail to understand why Python has different scope approach depending on variable type?
    • mmgp
      mmgp over 11 years
      You haven't understood the difference between mutable and immutable objects in Python. If you read docs.python.org/2/reference/datamodel.html (at least the subsection 3.1, don't skip parts of it just because you think you already know it) then it should become clear.
    • Martijn Pieters
      Martijn Pieters over 11 years
      There is nothing specific to Python 3 in this question.
  • Jovik
    Jovik over 11 years
    I understand I can do it (and mechanism behind it). What bothers me is why Python makes exceptions for global statement?
  • Martijn Pieters
    Martijn Pieters over 11 years
    @Jovik: There is no exception being made. You are confused between variable assignment and value manipulation. You cannot do var = 'string', var.insert(0, 'othertext') because a string is not mutable, you can only replace the value the variable points to. But you can alter a mutable. You can do var1 = {}, var2 = var1, var2.update({'another': 'dict'}) and you'll see that var1 reflects that same change. You changed a mutable value, not the variables that point to it.
  • mmgp
    mmgp over 11 years
    stringvar = "bar" is only ambiguous if you are considering a language other than Python. The only barely positive message someone can take from this is answer is the bottom-most link.
  • David Robinson
    David Robinson over 11 years
    @mmgp: I think you misunderstand me. I'm not saying the behavior is ambiguous or undefined. The OP was asking why Python behaves as it does, and I was explaining why it doesn't have a choice- because it can't know when you have the line stringvar = "bar" whether you mean to refer to the global variable or create a local variable. It therefore defaults to creating a local variable.
  • Jin
    Jin over 5 years
    So if there is a local dictionary also called dictvar, would that get invoked first before the global one?
  • David Robinson
    David Robinson over 5 years
    @Jin yes, that’s right!