# 160. Deep Copy vs Shallow Copy

***

**Snippet 1: Shallow Copy Using `copy()` Method**

```python
import copy

# Original list with nested list
original_list = [1, 2, [3, 4]]

# Shallow copy
shallow_copy = original_list.copy()

# Modifying the nested list in the shallow copy
shallow_copy[2][0] = 99

print(f"Original list: {original_list}")
print(f"Shallow copy: {shallow_copy}")
```

***

**Snippet 2: Shallow Copy Using `copy.copy()`**

```python
import copy

# Original dictionary with nested dictionary
original_dict = {'a': 1, 'b': {'c': 2}}

# Shallow copy
shallow_copy = copy.copy(original_dict)

# Modifying the nested dictionary in the shallow copy
shallow_copy['b']['c'] = 99

print(f"Original dict: {original_dict}")
print(f"Shallow copy: {shallow_copy}")
```

***

**Snippet 3: Deep Copy Using `copy.deepcopy()`**

```python
import copy

# Original list with nested list
original_list = [1, 2, [3, 4]]

# Deep copy
deep_copy = copy.deepcopy(original_list)

# Modifying the nested list in the deep copy
deep_copy[2][0] = 99

print(f"Original list: {original_list}")
print(f"Deep copy: {deep_copy}")
```

***

**Snippet 4: Comparing Shallow and Deep Copy**

```python
import copy

# Original list with nested list
original_list = [1, 2, [3, 4]]

# Shallow copy
shallow_copy = original_list.copy()

# Deep copy
deep_copy = copy.deepcopy(original_list)

# Modify nested list
shallow_copy[2][0] = 99
deep_copy[2][0] = 100

print(f"Original list: {original_list}")
print(f"Shallow copy: {shallow_copy}")
print(f"Deep copy: {deep_copy}")
```

***

**Snippet 5: Shallow Copy of a Nested List**

```python
# Original nested list
original_list = [1, [2, 3], 4]

# Shallow copy using list() constructor
shallow_copy = list(original_list)

# Modify nested list
shallow_copy[1][0] = 99

print(f"Original list: {original_list}")
print(f"Shallow copy: {shallow_copy}")
```

***

**Snippet 6: Shallow Copy of a Dictionary**

```python
# Original dictionary with nested dictionary
original_dict = {'key1': 1, 'key2': {'subkey1': 10}}

# Shallow copy using dict() constructor
shallow_copy = dict(original_dict)

# Modify nested dictionary
shallow_copy['key2']['subkey1'] = 99

print(f"Original dict: {original_dict}")
print(f"Shallow copy: {shallow_copy}")
```

***

**Snippet 7: Creating a Deep Copy with a Custom Class**

```python
import copy

# Custom class with a nested list
class MyClass:
    def __init__(self):
        self.data = [1, 2, [3, 4]]

# Original object
original_obj = MyClass()

# Deep copy
deep_copy = copy.deepcopy(original_obj)

# Modify nested list in the deep copy
deep_copy.data[2][0] = 99

print(f"Original object: {original_obj.data}")
print(f"Deep copy: {deep_copy.data}")
```

***

**Snippet 8: Shallow Copy and Identity**

```python
# Original list with nested list
original_list = [1, 2, [3, 4]]

# Shallow copy
shallow_copy = original_list.copy()

# Check identities of original and shallow copy
print(f"Original list id: {id(original_list)}")
print(f"Shallow copy id: {id(shallow_copy)}")
```

***

**Snippet 9: Deep Copy and Identity**

```python
import copy

# Original list with nested list
original_list = [1, 2, [3, 4]]

# Deep copy
deep_copy = copy.deepcopy(original_list)

# Check identities of original and deep copy
print(f"Original list id: {id(original_list)}")
print(f"Deep copy id: {id(deep_copy)}")
```

***

**Snippet 10: When to Use Shallow Copy vs Deep Copy**

```python
import copy

# Shallow copy is useful when you want to copy the top-level structure but share references for nested objects
original_list = [1, 2, [3, 4]]
shallow_copy = original_list.copy()
shallow_copy[2][0] = 99
print(f"Shallow copy modifies nested: {original_list} -> {shallow_copy}")

# Deep copy is useful when you want to completely clone an object without shared references
deep_copy = copy.deepcopy(original_list)
deep_copy[2][0] = 100
print(f"Deep copy does not affect original: {original_list} -> {deep_copy}")
```

***
