Python: Modify Global list inside a function

First of all, I understand that I can use global statement to access global variables. But somehow I was able to modify a global list without global like below:

def func1(nums):
    nums = [4,5,6]

nums = [1,2,3]
func1(nums)
print nums # print [1,2,3]

def func2(nums):
    nums[0] = 4
    nums[1] = 5
    nums[2] = 6

nums = [1,2,3]
func2(nums)
print nums # print [4,5,6]

After trying func2, I realized that I can always access global list in a function if I specify the index:

def func3(nums):
    nums[:] = [4,5,6]

nums = [1,2,3]
func3(nums)
print nums # print [4,5,6]

Is it because Python automatically go trying to match a global variable if a function variable is used before definition?


Solution 1:

I understand that I can use global statement to access global variables

Your understanding is wrong. 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. In your func2, you are not doing that; you are only changing the contents of the object. nums still refers to the same list.

Solution 2:

It is of concept based on mutable and immutable objects in Python. In your case, for example:

a=[1,2]
def myfn():
    a=[3,4]
    print id(a)

>>>id(a)
3065250924L
>>>myfn()
3055359596

It is clear both are different objects. Now:

a=[1,2]
def myfn():
    a[:] =[3,4]
    print id(a)

>>>id(a)
3055358572
>>>myfn()
3055358572

That means it is same variable using in local and global scope.

Solution 3:

In this specific case it is because lists are mutable.

As a result having them in the global namespace, or even passed through a function, means that they will be changed as Python holds the reference to the mutable object, not a copy of it.

If you try the same thing with tuples it will not work, as they are immutable.

The way to avoid this is to provide a copy of the list to the function, not the list itself:

func2(list[:])

At the same time you can do this with default arguments, where you can specify a default argument to be [], and if you then .append() something to it, that default argument will forever hold that item within it for all future calls (unless you remove it in some way).