You have hit two concepts there: Python's "call by assignment" and "shallow copy".
In Python, passing immutable types (like str
) to a function, a copy of that argument is actually sent to the function. So modifying it won't affect the original variable.
Eg:
def func(my_str):
my_str = "changed_string"
inp = "abcd"
print(inp) # abcd
func(inp)
print(inp) # abcd
But passing mutable types like list
will not pass a copy of it, it rather send a reference to that list (think of like a pointer to the list). So modifying a list coming in as an argument will modify the original list.
Eg:
def func(my_list):
my_list += [4,5,6] # notice the += operator
inp = [1,2,3]
print(inp) # [1,2,3]
func(inp)
print(inp) # [1,2,3,4,5,6]
But, if you reassign the incoming argument, then that won't affect the original variable.
Eg:
def func(my_list):
my_list = [4,5,6]
inp = [1,2,3]
print(inp) # [1,2,3]
func(inp)
print(inp) # [1,2,3]
Now that call-by-assignment is clear, let's look at shallow and deep copy.
In you code, the following part is a Shallow copy:
l_01 = l_0
l_11 = l_1
Here, both l_01
and l_0
point to the same list. So using both variables will eventually modify the same list.
Eg:
my_list1 = [1,2,3]
print(my_list1) # [1,2,3]
my_list2 = my_list1 # SHALLOW COPY
print(my_list2) # [1,2,3]
my_list2[0] = 10
print(my_list1) # [10,2,3]
print(my_list2) # [10,2,3]
Now, to avoid this, you have to do a deepcopy. One example for list would be to use the list()
constructor.
Eg:
my_list1 = [1,2,3]
print(my_list1) # [1,2,3]
my_list2 = list(my_list1) # DEEP COPY (not recursive deep copy!!)
print(my_list2) # [1,2,3]
my_list2[0] = 10
print(my_list1) # [1,2,3]
print(my_list2) # [10,2,3]
There are several ways to do deep copy: using the slicing operator, using the copy
library etc. Using the copy.deepcopy
method to make copies ensures recursive deep copies.