j3958402
j3958402

Reputation: 13

Difference between global and local variable

Am new to python and was wondering difference between having an empty array as global or local variable when returning the array.

import collections

def check_duplicates(data: list) -> list:
    dupes = []

    for i in data:
        if data.count(i) > 1:
        dupes.append(i)
    return dupes

if __name__ == "__main__":
assert list(check_duplicates([1, 2, 3, 4, 5])) == [],

Your Result:[]
Right Result:[]

import collections
dupes = []

def check_duplicates(data: list) -> list:

    for i in data:
        if data.count(i) > 1:
        dupes.append(i)
    return dupes

if __name__ == "__main__":
assert list(check_duplicates([1, 2, 3, 4, 5])) == [],

Your result:[1,3,1,3]
Right result:[]

Originally I had the array outside of the function and was unable to return an empty array and was wondering

Upvotes: 1

Views: 66

Answers (1)

Paritosh Singh
Paritosh Singh

Reputation: 6246

Alright, so first things first, the original code behaves correctly for how its written (but not in the way you wanted it), and will stop giving empty arrays once you run it for a list that produces a non empty array. The only thing left is to understand why it behaved the way it did.

Explanation

First, lists are mutable datatypes.

a = [1,2] #create a list object and bind it to the name a
b = a #both b and a are now referring to the same list object
a.append(3) #the list object a was referring to has been changed inplace, or mutated.
#notice that coincidentally b was referring to this same object
print(a) #Prints: [1,2,3]
print(b) #Prints: [1,2,3]

What that means is that if you use a list's method to make changes, such as .append, it will change the list object inplace, and all names (such as a or b) referring to it will reflect the change.

Second, Your original code, with some comments

dupes = [] #create an empty list, and have the name dupes refer to the list object

def check_duplicates(data: list) -> list:


    for i in data:
        if data.count(i) > 1:
            dupes.append(i) #mutate the list object that dupes was referring to.
    return dupes #return the list object dupes was referring to.

What is important is to realise that throughout the entire function, you never reassign the name dupes so it continues to refer to the same list object. What it results in is behaviour as follows:

dupes = []

def check_duplicates(data: list) -> list:


    for i in data:
        if data.count(i) > 1:
            dupes.append(i)
    return dupes

check_duplicates([1])
print(dupes) #[]
check_duplicates([2])
print(dupes) #[]
check_duplicates([1, 1])
print(dupes) #[1, 1]
check_duplicates([2])
print(dupes) #[1, 1] !!!! The dupes list continues to refer to the object, and any further append calls just add to it
check_duplicates([99, 99, 100, 100])
print(dupes) #[1, 1, 99, 99, 100, 100]
check_duplicates([2])
print(dupes) #[1, 1, 99, 99, 100, 100]

Hopefully that makes it clearer. the dupes name will continue to refer to the list that is being mutated inside the function, and this is why unless you reassign dupes to a new empty list inside, you will continue to get the old results. Hope this helps. Good further reading.

Upvotes: 2

Related Questions