First, some terminology:
In Python, arguments are passed by assignment (as opposed to other languages, where arguments can be passed by value/reference/pointer).
Mutating a parameter will mutate the argument (if the argument's type is mutable).
def foo(x): # here x is the parameter x = 9 # This mutates the list labelled by both x and y print(x) y = [4, 5, 6] foo(y) # call foo with y as argument # Out: [9, 5, 6] # list labelled by x has been mutated print(y) # Out: [9, 5, 6] # list labelled by y has been mutated too
Reassigning the parameter won’t reassign the argument.
def foo(x): # here x is the parameter, when we call foo(y) we assign y to x x = 9 # This mutates the list labelled by both x and y x = [1, 2, 3] # x is now labeling a different list (y is unaffected) x = 8 # This mutates x's list, not y's list y = [4, 5, 6] # y is the argument, x is the parameter foo(y) # Pretend that we wrote "x = y", then go to line 1 y # Out: [9, 5, 6]
In Python, we don’t really assign values to variables, instead we bind (i.e. assign, attach) variables (considered as names) to objects.
x = [3, 1, 9] y = x x.append(5) # Mutates the list labelled by x and y, both x and y are bound to [3, 1, 9] x.sort() # Mutates the list labelled by x and y (in-place sorting) x = x +  # Does not mutate the list (makes a copy for x only, not y) z = x # z is x ([1, 3, 9, 4]) x +=  # Mutates the list labelled by both x and z (uses the extend function). x = sorted(x) # Does not mutate the list (makes a copy for x only). x # Out: [1, 3, 4, 5, 6, 9] y # Out: [1, 3, 5, 9] z # Out: [1, 3, 5, 9, 4, 6]