Using copy

https://docs.python.org/3/library/copy.html

INTRODUCTION

This module provides generic shallow and deep copy operations.
A shallow copy constructs a new compound object and then inserts references into it to the objects found in the original.
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.

Two problems with deep copy operations:
(a) recursive objects may cause a recursive loop,
(b) it may copy too much, such as data which is intended to be shared between copies.

The deepcopy() function avoids these problems by:
(a) keeping a memo dictionary of objects already copied during the current copying pass,
(b) letting user-defined classes override the copying operation or the set of components copied.


import copy

# copy.copy(obj) - return a shallow copy of obj,
# copy.deepcopy(obj[, memo]) - return a deep copy of obj,
# copy.replace(obj, /, **changes) - creates a new object of the same type
#     as obj, replacing fields with values from 'changes' (Py3.13).

M = [1, 2, 3]
L = [10, "a", M]
L2 = copy.copy(L)   # or L2 = L.copy(), or L2 = L[:]
assert id(L) != id(L2)
assert id(L[1]) == id(L2[1])
assert id(L[2]) == id(L2[2])
L3 = copy.deepcopy(L)
assert id(L) != id(L3)
assert id(L[1]) == id(L3[1])   # strings are immutable
assert id(L[2]) != id(L3[2])
M[1] = 22
print(L)   # [10, 'a', [1, 22, 3]]
print(L2)   # [10, 'a', [1, 22, 3]]
print(L3)   # [10, 'a', [1, 2, 3]]   # no changes

# http://stackoverflow.com/questions/1500718/
# what-is-the-right-way-to-override-the-copy-deepcopy-operations-on-an-object-in-p

class MyClass(object):
    def __init__(self): ...
    def __copy__(self): ...
    def __deepcopy__(self, memo): ...