Difference between arr.append(curr) and arr.append(curr[:])

Viewed 211

Function to produce powerset gives the correct input. However upon replacing append(curr[:]) with append(curr) gives a list of empty lists. What is the reason?

def subsets(nums):
    def backtrack(first = 0, curr = []):
        # if the combination is done
        if len(curr) == k:  
            output.append(curr)
            return
        for i in range(first, n):
            # add nums[i] into the current combination
            curr.append(nums[i])
            # use next integers to complete the combination
            backtrack(i + 1, curr)
            # backtrack
            curr.pop()
    
    output = []
    n = len(nums)
    for k in range(n + 1):
        backtrack()
    return output
3 Answers

As other answers have noted, cur[:] makes a copy of cur. Without making a copy output ends up containing many copies of the same list - each of those lists are modified every time you call cur.append or cur.pop.

The lists end up empty because there is one call to cur.pop for every call of cur.append, which ensures that the "final" iteration of cur is going to be empty.

The difference between arr.append(curr) and arr.append(curr[:]) is that the first one copy the curr list address to arr and the second one copy the values of curr to arr.

Let's see the difference using examples.

First scenario:

curr = [5, 10, 15, 20]
arr = []
arr.append(curr)
print("Before update")
print("Curr: {}\nArr: {}".format(curr, arr))
curr[1] = 7
print("After update")
print("Curr: {}\nArr: {}".format(curr, arr))

Output:

Before update
Curr: [5, 10, 15, 20]
Arr: [[5, 10, 15, 20]]
After update
Curr: [5, 7, 15, 20]
Arr: [[5, 7, 15, 20]]

Second scenario:

curr = [5, 10, 15, 20]
arr = []
print("Before update")
arr.append(curr[:])
print("Curr: {}\nArr: {}".format(curr, arr))
curr[1] = 7
print("After update")
print("Curr: {}\nArr: {}".format(curr, arr))

Output:

Before update
Curr: [5, 10, 15, 20]
Arr: [[5, 10, 15, 20]]
After update
Curr: [5, 7, 15, 20]
Arr: [[5, 10, 15, 20]]

As you can see in first scenario both curr and arr is updated upon changing one index value of the curr list. Because appending the curr to arr holds the same memory address.

In second scenario only curr is updated upon changing one index value of the curr list. Because appending [:] to arr copies the contents to curr list to arr list.

According to said above(theory), the reason why you get an [[], [], [], [], [], [], [], []] when you run the code changing curr for curr[:] is because in the last line curr.pop() in backtrack function you're removing the last elements one by one so you're taking off the elements from the list which is located at same space memory all the time and not from a copy list values of it.

Using arr.append(curr[:]) means append a COPY of curr while arr.append(curr) append curr itself to the array arr, meaning when you make any change to curr, it's value in arr will also change correspondingly

You can read more about array copy here: Link

Related