Search code examples
pythonlistmutable

Python: List in global namespace gets unintentionally modified through function


I have a propably very basic problem in Python. However, I would be very thankful, if someone could help me to understand, what is happening here:

My code is as follows:

purchaseprices = {'Stock_A': [[10, 4.21],[20, 5.23], [5, 8.32]],
                  'Stock_B': [[5, 8.23],[15, 7.42], [10, 7.53]]}



def update_purchaseprices(name, number_of_shares):
    remaining_number_of_shares = number_of_shares
    updated_purchaseprices = purchaseprices[name][:]
    indices_to_delete = []
    for i in range(len(updated_purchaseprices)):
        if updated_purchaseprices[i][0] < remaining_number_of_shares:
            remaining_number_of_shares -= updated_purchaseprices[i][0]
            indices_to_delete.append(i)
        else:
            updated_purchaseprices[i][0] = updated_purchaseprices[i][0] - remaining_number_of_shares
            break
    updated_purchaseprices = [i for j, i in enumerate(updated_purchaseprices) if j not in indices_to_delete]
    return updated_purchaseprices

name = "Stock_A"
number_of_shares = 34

print(purchaseprices['Stock_A'])
updated_purchaseprices = update_purchaseprices(name, number_of_shares)
print(updated_purchaseprices) # this works as expected
print(purchaseprices['Stock_A']) # why did the original list got changed as well?

Here is, what I wanted to do: I have an original list, which is stored in a dictionary called purchaseprices. This list can be accessed by purchaseprices['Stock_A’]. Now I tried to write a function to return a list called updated_purchaseprices, which is basically a modified version of the original list. In order to leave the original list unchanged, I made a copy of it by including updated_purchaseprices = purchaseprices[name]:. Unfortunately my code nevertheless also changes the original list. Can someone please tell me why this is happening?


Solution

  • As you probably know because you used [:], a list is mutable and you need to take a copy in your function. But the copy still contains the original objects (sublists).

    You need to copy those too!

    Replace:

    updated_purchaseprices = purchaseprices[name][:]
    

    with:

    updated_purchaseprices = [l.copy() for l in purchaseprices[name]]
    

    or:

    import copy
    updated_purchaseprices = copy.deepcopy(purchaseprices[name])