1

I'm a bit confused about the behavior of global variables with the use if setdefault() in python:

Please find sample code for how this are referenced/resolved using setdefault, can somebody help me clarify what is going on in here?

Main file where variables are resoved :

#main
from test import *

fill_func()

print my_list
print my_dict

Test file where variables are assigned values :

#test

my_list = []
my_dict = {}

def fill_func():
    my_list = [1,2,3]
    print my_list
    my_dict.setdefault(0,[]).append('zero')
    print my_dict

Output :

[1, 2, 3]
{0: ['zero']}
[]
{0: ['zero']}

I'm not able to understand why the list(my_list) variable shows empty when called from main.py, whereas my_dict shows data fine?

Any help is appreciated. TIA!

#

Sample test file 2

#test

my_list = []
my_dict = {}

def fill_func():
    def fill_list():
        global my_list
        my_list = [1,2,3]
        print my_list
    my_dict.setdefault(0,[]).append('zero')
    print my_dict
    fill_list()

Ouput :

{0: ['zero']}
[1, 2, 3]
[]
{0: ['zero']}

Can someone please throw some light on the second test file, please bear with me, trying to understand the basics :)

TIA!

R2D2
  • 298
  • 2
  • 12

4 Answers4

2

my_list is defined as a local variable inside the fill_func; this is shadowing the other my_list defined in the global scope.

therefore, your code first calls the fill_func that prints the local my_list, then the default dict. It then exits the function and prints the outer scope my_list, and the dict (which was not shadowed)

Reblochon Masque
  • 35,405
  • 10
  • 55
  • 80
1

You are creating a local variable with the same name as the global variable. Just add global my_list at the top of the function.

MegaIng
  • 7,361
  • 1
  • 22
  • 35
1

That's because, as you've rightly indicated, it has to do with scope. my_list and my_dict are global to test.py, which have to accessed using the global qualifier. That is, your code should be:

# Edited to address comments (see explanation below)
def fill_func():
    global my_list  # this is necessary since you're updating the values
    global my_dict
    my_list.extend([1,2,3])
    print my_list
    my_dict.setdefault(0,[]).append('zero')
    print my_dict

EDIT:

To get the both the list and the dictionary to update, one has to extend the list, and modify the dictionary (as you've done) - i.e. actually change its value. Assigning it a value using the assignment operator only changes what the variable refers to and not the value itself. This is why it doesn't update outside of the local function scope. And, this is also why it updates when we use other methods to modify the contents of those variables.

The problem is when a function body is parsed all the variables used in either normal assignments or augmented assigments are considered as local variables, so when the function gets called Python will not look for those variables in global scope hence it will raise an error. Hence you need to specify those variables as global to tell Python to look for them in global scope.

Another alternative is to use list.extend()

(From here: https://stackoverflow.com/a/23436510/866930 . An additional reference that's useful on this is: https://stackoverflow.com/a/31437415/866930)

You can always access a global variable as long as you don't have a local variable of the same name. You only need the global statement when you are going to change what object a variable name refers to.

Compare to this version:

def fill_func():
    global my_list
    global my_dict
    my_list = [1,2,3]
    print my_list  # prints `[1, 2, 3]` here and `[]` in main
    my_dict = {1: 'a', 2: 'b'}
    print my_dict  # prints `{1: 'a', 2: 'b'}` here, and `{}` in main

Without the use of global, Python would think that the variable is local to the scope of the code element where it was defined, hence global tells the interpreter to look within the globals symbol table (these contents can be accessed via globals())

jrd1
  • 10,358
  • 4
  • 34
  • 51
  • the solution you proposed is ain't working, I think this shd work fine but its not!, also can you commedt on edit 2 – R2D2 Oct 03 '17 at 06:42
  • @R2D2: Thanks for the heads-up! Edited. – jrd1 Oct 03 '17 at 07:10
  • But with the usage of global as well I'm not able to modify the list which I'm referring to, list assignment in function creates a new reference even though I'm using global inside the function – R2D2 Oct 03 '17 at 07:31
  • @R2D2: correct - that's what happens in the second example. In the edit where I entered the second example I also fixed the first one (i.e. using an `extend`, so now the lists contents are properly updated). Are you getting a similar error using that version? – jrd1 Oct 03 '17 at 07:37
0

you need to declare in your function that you want to use the global variable.

You can do that by

def fill_func():
    global my_list
    global my_dict
    my_list = [1,2,3]
    print my_list
    my_dict.setdefault(0,[]).append('zero')
    print my_dict

Please note that normally it is preferred to use only CAPITAL LETTERS for globals to avoid confusion

Anderas
  • 630
  • 9
  • 20