Search code examples
pythoncopyreturn-valuedeep-copy

In python, returning an object created in a function body makes a deep copy of it?


I'll try to clarify:

For example, I make a function that locally creates a list, and return it. How does Python create the returned list that exist outside the function body ? Does it use "deepcopy" (or something similar) ?

In [50]: def create_list():
    ...:     sublist1 = [1,2,3]
    ...:     sublist2 = [4,5,6]
    ...:     list_of_lists=[sublist1,sublist1,sublist2]
    ...:     return list_of_lists
    ...: 

In [51]: l=create_list()

In [52]: l
Out[52]: [[1, 2, 3], [1, 2, 3], [4, 5, 6]]

In [53]: l[0].append(4)

In [54]: l
Out[54]: [[1, 2, 3, 4], [1, 2, 3, 4], [4, 5, 6]]

Here, the returned list l still contains the sublists. And l[0] and l[1] still reference the same sublist (which is normal Python behavior). So the list and its structure were copied.

And if I call once again create_list() :

In [55]: l2=create_list()

In [56]: l2
Out[56]: [[1, 2, 3], [1, 2, 3], [4, 5, 6]]

In [57]: l
Out[57]: [[1, 2, 3, 4], [1, 2, 3, 4], [4, 5, 6]]

A new list l2 has been created, but l is unaffected, which means it does exist outside the function, and its sublists are its own, not references to sublists that would still exist in the function body.

So my question : does Python used deepcopy or something similar to make l ? Not matter what kind of object I return with a function, it will be unaffected by subsequent call to this function ? (as long as the object was created locally in the function)

Do not hesitate to tell me if I'm not clear enough. Thanks,


Solution

  • When you run the function the second time, the entire function is rerun - it has no memory along the lines of "last time, sublist1 was [1, 2, 3]".

    You haven't copied the list [1, 2, 3]. You've created it twice.


    Note that if you use a caching decorator like @functools.lru_cache, you'll get surprising results:

    >>> @lru_cache()
    ... def create_list():
    ...     sublist1 = [1,2,3]
    ...     sublist2 = [4,5,6]
    ...     list_of_lists=[sublist1,sublist1,sublist2]
    ...     return list_of_lists
    ...
    >>> l = create_list(); l
    [[1, 2, 3], [1, 2, 3], [4, 5, 6]]
    >>> l[0].append(4); l
    [[1, 2, 3, 4], [1, 2, 3, 4], [4, 5, 6]]
    >>> create_list()
    [[1, 2, 3, 4], [1, 2, 3, 4], [4, 5, 6]]
    

    Because in this case, python does have a memory of the previous result, and returns the same object