Your code does indeed succeed in creating a shallow copy. This can be seen by inspecting the IDs of the two outer lists, and noting that they differ.

>>> id(l)
140505607684808

>>> id(x)
140505607684680

Or simply comparing using is:

>>> x is l
False

However, because it is a shallow copy rather than a deep copy, the corresponding elements of the list are the same object as each other:

>>> x[0] is l[0]
True

This gives you the behaviour that you observed when the sub-lists are appended to.

If in fact what you wanted was a deep copy, then you could use copy.deepcopy. In this case the sublists are also new objects, and can be appended to without affecting the originals.

>>> from copy import deepcopy

>>> l=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

>>> xdeep = deepcopy(l)

>>> xdeep == l
True

>>> xdeep is l
False     <==== A shallow copy does the same here

>>> xdeep[0] is l[0]
False     <==== But THIS is different from with a shallow copy

>>> xdeep[0].append(10)

>>> print(l)
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

>>> print(xdeep)
[[1, 2, 3, 10], [4, 5, 6], [7, 8, 9]]

If you wanted to apply this in your function, you could do:

from copy import deepcopy

def processed(matrix,r,i):
    new_matrix = deepcopy(matrix)
    new_matrix[r].append(i)
    return new_matrix

l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
x = processed(l,0,10)
print(x)
print(l)

If in fact you know that the matrix is always exactly 2 deep, then you could do it more efficiently than using deepcopy and without need for the import:

def processed(matrix,r,i):
    new_matrix = [sublist[:] for sublist in matrix]
    new_matrix[r].append(i)
    return new_matrix

l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
x = processed(l,0,10)
print(x)
print(l)
Answer from alani on Stack Overflow
๐ŸŒ
Python
docs.python.org โ€บ 3 โ€บ library โ€บ copy.html
copy โ€” Shallow and deep copy operations
A shallow copy constructs a new ... 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....
๐ŸŒ
GeeksforGeeks
geeksforgeeks.org โ€บ python โ€บ copy-python-deep-copy-shallow-copy
Deep Copy and Shallow Copy in Python - GeeksforGeeks
In Python, assignment statements create references to the same object rather than copying it. Python provides the copy module to create actual copies which offer functions for shallow (copy.copy()) and deep (copy. deepcopy ()) copies.
Published: February 5, 2026
Discussions

deep copy of list in python - Stack Overflow
pythonjavascriptc#reactjsjavaandroidhtmlflutterc++node.jstypescriptcssrphpangularnext.jsspring-bootmachine-learningsqlexceliosazuredocker ... Next Youโ€™ll be prompted to create an account to view your personalized homepage. Find centralized, trusted content and collaborate around the technologies you use most. Learn more about Collectives ... Save this question. Show activity on this post. ... I tried to make a deep copy ... More on stackoverflow.com
๐ŸŒ stackoverflow.com
How can I make a deepcopy of a function in Python? - Stack Overflow
I would like to make a deepcopy of a function in Python. The copy module is not helpful, according to the documentation, which says: More on stackoverflow.com
๐ŸŒ stackoverflow.com
object oriented - Shouldn't deep copy be the default, not shallow copy? - Software Engineering Stack Exchange
If you have an OO language, where every object always has a copy method, shouldn't that be deep copy by default? In most languages I know, such a copy method is shallow, since a shallow copy is more More on softwareengineering.stackexchange.com
๐ŸŒ softwareengineering.stackexchange.com
October 17, 2023
Python copy.copy() appears to make a deep copy
The difference between a deep copy and a shallow copy is whether or not nested mutable structures are (recursively) copied (deep) or just referenced (shallow). All elements of your list are immutable. Therefore, there is no difference between a shallow and a deep copy. A proper demonstration of the difference is this: import copy my_list = [['first'], ['second'], ['third']] my_deep_copy = copy.deepcopy(my_list) my_deep_copy[1][0] = 'new value' print(my_list) my_shallow_copy = copy.copy(my_list) my_shallow_copy[1][0] = 'new value' print(my_list) Note how it requires nested mutable structures. More on reddit.com
๐ŸŒ r/learnpython
10
90
October 17, 2022
๐ŸŒ
Reddit
reddit.com โ€บ r/learnpython โ€บ python copy.copy() appears to make a deep copy
r/learnpython on Reddit: Python copy.copy() appears to make a deep copy
October 17, 2022 -

I am following the python docs for copy functions to learn how to make a deep copy of a list or dictionary. (https://docs.python.org/3/library/copy.html) However, when I use copy.copy(list) and change one of the values it doesn't modify the original list like I thought.

Example

import copy

myL = ['first', 'second', 'third']

myLReg = copy.copy(myL)

myLReg[1] = 'new value'

console.log(myL)

What I expect the output to be is ['first', 'new value', 'third'] but I get ['first', 'second', 'third']

Am I understanding the docs wrong, is there a bug, or something else?

This is the behavior I expected from copy.deepcopy(myL)

What does copy.deepcopy() do? Apr 26, 2024
r/learnpython
2y ago
Why is copying a list so damn difficult in python? Mar 16, 2013
r/learnpython
13y ago
copy vs deepcopy Nov 6, 2020
r/learnpython
5y ago
More results from reddit.com
๐ŸŒ
Python for the Lab
pythonforthelab.com โ€บ blog โ€บ deep-and-shallow-copies-of-objects
Deep and Shallow Copies of Objects | Python For The Lab
If you change copy by deepcopy, the behavior would change, exactly in the same way than with lists or dictionaries. But we can go one step further, and customize the behavior of the shallow or deep copies of objects. With Python, you have a very high level of granularity regarding how much control you have on every step, including deep and shallow copies.
๐ŸŒ
Real Python
realpython.com โ€บ ref โ€บ glossary โ€บ deep-copy
deep copy | Python Glossary โ€“ Real Python
In Python, deep copy refers to creating a new object that is a complete, independent clone of the original object, including all objects it refers to, recursively.
Find elsewhere
Top answer
1 of 3
12

Your code does indeed succeed in creating a shallow copy. This can be seen by inspecting the IDs of the two outer lists, and noting that they differ.

>>> id(l)
140505607684808

>>> id(x)
140505607684680

Or simply comparing using is:

>>> x is l
False

However, because it is a shallow copy rather than a deep copy, the corresponding elements of the list are the same object as each other:

>>> x[0] is l[0]
True

This gives you the behaviour that you observed when the sub-lists are appended to.

If in fact what you wanted was a deep copy, then you could use copy.deepcopy. In this case the sublists are also new objects, and can be appended to without affecting the originals.

>>> from copy import deepcopy

>>> l=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

>>> xdeep = deepcopy(l)

>>> xdeep == l
True

>>> xdeep is l
False     <==== A shallow copy does the same here

>>> xdeep[0] is l[0]
False     <==== But THIS is different from with a shallow copy

>>> xdeep[0].append(10)

>>> print(l)
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

>>> print(xdeep)
[[1, 2, 3, 10], [4, 5, 6], [7, 8, 9]]

If you wanted to apply this in your function, you could do:

from copy import deepcopy

def processed(matrix,r,i):
    new_matrix = deepcopy(matrix)
    new_matrix[r].append(i)
    return new_matrix

l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
x = processed(l,0,10)
print(x)
print(l)

If in fact you know that the matrix is always exactly 2 deep, then you could do it more efficiently than using deepcopy and without need for the import:

def processed(matrix,r,i):
    new_matrix = [sublist[:] for sublist in matrix]
    new_matrix[r].append(i)
    return new_matrix

l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
x = processed(l,0,10)
print(x)
print(l)
2 of 3
1

What you're looking for is a deeper copy than what you did. A shallow copy only replaces the top layer, which does not seem to be what you're looking for. If you wanted a different outcome, try something like this:

def processed(matrix,r,i):
    matrix[r] = [*matrix[r], i]
    return matrix

l=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
x=l[:]
print(processed(x,0,10))
print(l)

The difference is that this makes two shallow copies - first to copy the outer list, and then the function copies the inner list before modifying it. The downside of this approach is that every call to processed now has extra overhead. If you wanted to do the copying all at once, you can do this:

def processed(matrix,r,i):
    matrix[r].append(i)
    return matrix

l=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
x=[inner[:] for inner in l]
print(processed(x,0,10))
print(l)

This copies two layers deep, using list comprehensions. Your structure only has two layers, so this fully copies the list.

๐ŸŒ
The Python Coding Book
thepythoncodingbook.com โ€บ home โ€บ blog โ€บ shallow and deep copy in python and how to use __copy__()
Shallow and Deep Copy in Python and How to Use __copy__()
November 12, 2023 - The article explores shallow and deep copy in Python, and how to use the __copy__() dunder method to customise the copying behaviour
๐ŸŒ
W3Schools
w3schools.com โ€บ python โ€บ ref_module_copy.asp
Python copy Module
The copy module provides shallow and deep copy operations for arbitrary Python objects.
๐ŸŒ
Readthedocs
pradyunsg-cpython-lutra-testing.readthedocs.io โ€บ en โ€บ latest โ€บ library โ€บ copy.html
copy โ€” Shallow and deep copy operations - Python 3.12.0a0 documentation
A shallow copy constructs a new compound object and then (to the extent possible) 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.
Top answer
1 of 9
34

The FunctionType constructor is used to make a deep copy of a function.

import types
def copy_func(f, name=None):
    return types.FunctionType(f.func_code, f.func_globals, name or f.func_name,
        f.func_defaults, f.func_closure)

def A():
    """A"""
    pass
B = copy_func(A, "B")
B.__doc__ = """B"""
2 of 9
32

My goal is to have two functions with the same implementation but with different docstrings.

Most users will do this, say the original function is in old_module.py:

def implementation(arg1, arg2): 
    """this is a killer function"""

and in new_module.py

from old_module import implementation as _implementation

def implementation(arg1, arg2):
    """a different docstring"""
    return _implementation(arg1, arg2)

This is the most straightforward way to reuse functionality. It is easy to read and understand the intent.

Nevertheless, perhaps you have a good reason for your main question:

How can I make a deepcopy of a function in Python?

To keep this compatible with Python 2 and 3, I recommend using the function's special __dunder__ attributes. For example:

import types

def copy_func(f, name=None):
    '''
    return a function with same code, globals, defaults, closure, and 
    name (or provide a new name)
    '''
    fn = types.FunctionType(f.__code__, f.__globals__, name or f.__name__,
        f.__defaults__, f.__closure__)
    # in case f was given attrs (note this dict is a shallow copy):
    fn.__dict__.update(f.__dict__) 
    return fn

And here's an example usage:

def main():
    from logging import getLogger as _getLogger # pyflakes:ignore, must copy
    getLogger = copy_func(_getLogger)
    getLogger.__doc__ += '\n    This function is from the Std Lib logging module.\n    '
    assert getLogger.__doc__ is not _getLogger.__doc__
    assert getLogger.__doc__ != _getLogger.__doc__

A commenter says:

This canโ€™t work for builtโ€‘in functions

Well I wouldn't do this for a built-in function. I have very little reason to do this for functions written in pure Python, and my suspicion is that if you are doing this, you're probably doing something very wrong (though I could be wrong here).

If you want a function that does what a builtin function does, and reuses the implementation, like a copy would, then you should wrap the function with another function, e.g.:

_sum = sum
def sum(iterable, start=0):
    """sum function that works like the regular sum function, but noisy"""
    print('calling the sum function')
    return _sum(iterable, start)
    
๐ŸŒ
Jhk Blog
jhkinfotech.com โ€บ home โ€บ shallow copy vs deep copy in python โ€“ what is the difference
Shallow Copy vs Deep Copy in Python - What is the Difference โ€“ Jhk Blog
February 17, 2026 - A deep copy in Python refers to the complete duplication of an object, including all the nested elements contained within it. Rather than merely copying the top-level structure, a deep copy dives recursively into the object hierarchy and creates ...
Top answer
1 of 10
76

Let's say we have a Car that has four Wheels and an Owner. If we want to deep-copy the Car, we'd probably want to copy the Wheels, but it probably wouldn't make much sense to copy the Owner (and thus copy everything else hanging off the Owner, possibly including the Car itself).

If object A has a reference to object B, it may be because A "owns" B, and B should be included in a deep copy. But it may also be because A merely has some kind of association with B, and B and shouldn't be included in a deep copy. Unless the language provides a way to distinguish between these two types of references, we can't automate a deep copy, and the logic will have to be explicitly specified.

2 of 10
32

Without keeping a big table of all the objects you've copied so far, you can't safely deep copy objects at all if there may be circular references.

where every object always has a copy method

Is this true of everything? In which languages? Doesn't this mess with e.g. lock objects? Streams? Use of the RAII pattern? Handles to operating system objects? Intentional singletons? There's lots of cases where you may legitimately want to prevent objects being copied at all.


I'd also like to push back on

Shouldn't the default always be optimized for safety and not for performance? Isn't optimizing copy for speed premature optimization?

Deep copy can get arbitrarily expensive. You can't overlook that entirely.

But there are lots of different opinions about "safety". Quite a lot of functional programming and FP influence in modern languages moves towards immutability. Objects once created aren't changed. This is a more mathematical view; if you have f(x) = y then it is always true that f(x) = y, and none of f, x, or y are mutable.

If an object is immutable, there is no point in copying it. You can just pass around references to it. You only ever produce mutated copies, instead of changing values within the object. You start thinking more like value types: nobody asks how many different copies of "2" there are in their program, nor do they care about deep vs shallow copy of "2".

Then we consider the reverse operation: interning. Sometimes people ask how many copies of the string "hello" there are in their program, and would find it convenient if all references to "hello" returned the same object. https://en.wikipedia.org/wiki/String_interning ; this makes deep copy meaningless, because in that situation you only ever want one copy of the interned string.

(Incidentally, it turns out that Java does care about how many copies of "2" there are in your program if you start boxing them - all Int(2) are the same object. But not all Int(2000).)

I expect that when I later on pass the same copy to the same method, it will behave exactly the same but that isn't guaranteed, as the objects the array references may have changed, despite me having never changed them in my code

Immutability fixes this.

In Haskell, the default containers are immutable, and it's quite hard to write a mutable one. You don't add an item to a list, you create a new list containing the contents plus the new item. https://www.fpcomplete.com/haskell/library/containers/

Rust takes a different approach: every reference is annotated with ownership and mutability semantics. So you can call methods confident in the knowledge that they won't mutate particular objects.

๐ŸŒ
EDUCBA
educba.com โ€บ home โ€บ software development โ€บ software development tutorials โ€บ python tutorial โ€บ python deep copy
Python deep copy | Complete Guide to Python deep copy with Examples
September 29, 2023 - We can use import copy or from copy import deep copy. We have seen in detail the Python deepcopy that is most popular in the Python programming platform because of its importance in working with duplicates or copies of original objects where the user can create a duplicate copy of an actual thing and make it independent from the original object to perform multiple operations or functions using the exact copy.
Address: Unit no. 202, Jay Antariksh Bldg, Makwana Road, Marol, Andheri (East),, 400059, Mumbai
๐ŸŒ
Statistics Globe
statisticsglobe.com โ€บ home โ€บ python programming language for statistics & data science โ€บ make deep copy of list in python (example)
Make Deep Copy of Python List | Without Changing Copied Object
July 5, 2023 - In this example, we have imported the copy module and used the copy.deepcopy() function to create a deep copy of the original list. The deepcopy() function creates a completely separate copy of the list, including any nested objects.
๐ŸŒ
Educative
educative.io โ€บ answers โ€บ how-to-make-a-deep-copy-in-python
How to make a deep copy in Python
Deep copy in Python is a process where each object or nested object gets copied entirely. In simple words, this means that any changes made to the deep copy will not reflect on the original one as they have saved separately at different memory ...
๐ŸŒ
Python
docs.python.org โ€บ 3 โ€บ library โ€บ collections.html
collections โ€” Container datatypes
In addition to the above, deques support iteration, pickling, len(d), reversed(d), copy.copy(d), copy.deepcopy(d), membership testing with the in operator, and subscript references such as d[0] to access the first element. Indexed access is O(1) at both ends but slows to O(n) in the middle.
๐ŸŒ
Intellipaat
intellipaat.com โ€บ home โ€บ blog โ€บ deep copy and shallow copy in python
Deep Copy and Shallow Copy in Python: Examples & Differences
December 15, 2025 - A Deep Copy in Python is used to create a brand new object and fills it with copies of all the items from the original object. This includes the nested objects, one by one. This means that it goes through each item inside the original object ...