What is the difference between a = b and a = b[:]?

ghz 23hours ago ⋅ 2 views

Just trying to understand the difference between shallow copies. Say I have a list:

lst = [1,2,3] 
a = lst
b = lst[:]

Can someone please explain the difference between these shallow copy methods.

Answer

Certainly! The difference between the two methods you've shown for creating shallow copies of a list is subtle but important. Let's break it down:

1. a = lst (No copy, just a reference)

When you do:

lst = [1, 2, 3]
a = lst

Here, a is not a new list. It is just another reference to the same list lst. Both a and lst point to the same object in memory. Any changes made to a will affect lst, and vice versa, because they refer to the same underlying object.

Example:

lst = [1, 2, 3]
a = lst
a[0] = 99
print(lst)  # Output: [99, 2, 3]
print(a)    # Output: [99, 2, 3]

In this case, modifying a also modifies lst because both variables refer to the same list object.

2. b = lst[:] (Shallow copy using slicing)

When you do:

b = lst[:]

This creates a shallow copy of lst. The [:] syntax creates a new list object that contains the same elements as the original list, but b is now a separate object from lst (i.e., they are different objects in memory). However, the elements inside the list (in this case, the integers) are still references to the same objects. Since integers are immutable, it may not matter much in this specific case, but this distinction can become important when the list contains mutable objects.

Example:

lst = [1, 2, 3]
b = lst[:]  # Shallow copy of lst
b[0] = 99
print(lst)  # Output: [1, 2, 3]
print(b)    # Output: [99, 2, 3]

In this case, modifying b does not affect lst because b is a new list. However, if the list contained mutable objects (e.g., other lists or dictionaries), those mutable objects would still be shared between lst and b (because it's a shallow copy).

Summary of Key Differences:

  • a = lst: This doesn't create a new list. Instead, a and lst both refer to the same list object. Changes to one will affect the other.
  • b = lst[:]: This creates a new list that is a shallow copy of lst. The new list b has its own memory allocation, but it still contains references to the same objects as lst. Changes to b won't affect lst, but changes to mutable objects inside the list will be shared.

Shallow Copy vs. Deep Copy:

A shallow copy only copies the references to the objects in the original list, not the objects themselves. If the list contains nested mutable objects (like lists or dictionaries), both the original and the shallow copy will refer to the same nested objects.

A deep copy (created using copy.deepcopy(lst)) would copy both the outer list and all nested objects recursively, so there would be no shared references between the original and the copy.

Example of a shallow vs deep copy with nested lists:

import copy

lst = [[1, 2], [3, 4]]
shallow_copy = lst[:]  # Shallow copy
deep_copy = copy.deepcopy(lst)  # Deep copy

shallow_copy[0][0] = 99
print(lst)  # Output: [[99, 2], [3, 4]] (because the inner list is shared)
print(shallow_copy)  # Output: [[99, 2], [3, 4]]
print(deep_copy)  # Output: [[1, 2], [3, 4]] (deep copy is independent)

In this case, modifying the inner list in shallow_copy also affects lst because the inner lists are shared. However, the deep_copy is fully independent from the original lst.