7

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,

1
  • No but python does create new objects each time you run a function bar you pass a mutable default arg. Commented Apr 19, 2015 at 10:45

3 Answers 3

4

This might not directly answer this question but should help clarify related concept.

If you create a nested object inside a function and return it, the object will continue to exist. It would not go out of scope even though the function ended.

Example Code

class Some_Class (object): prop_x = None def __init__(self, prop_x ): self.prop_x = prop_x def __repr__(self): return "prop_x = "+repr (self.prop_x) def fx (): dict_x = { "k1" : "v1" } print hex ( id (dict_x) ) obj1 = Some_Class ( prop_x = dict_x ) print hex ( id (obj1.prop_x) ) print "obj1 is "+repr( obj1 ) return obj1 recv_obj = fx () print "recv_obj is "+repr( recv_obj ) print hex ( id (recv_obj.prop_x) ) 

Output

0xdfaae0 0xdfaae0 obj1 is prop_x = {'k1': 'v1'} recv_obj is prop_x = {'k1': 'v1'} 0xdfaae0 

A dict, dict_x, is assigned to prop_x variable of a Class object obj1. The dict is not created again in memory but a soft copy takes place. prop_x points to the memory location of dict_x.

When you return object obj1 at the end of this function, dict_x goes out of scope but the memory address used by it, 0xdfaae0, is still pointed by prop_x in the returned object recv_obj. So, the dict values { "k1" : "v1" } are preserved in memory.

Sign up to request clarification or add additional context in comments.

3 Comments

dict_x address was 0x2ff5540, prop_x address is 0x35c5540 . How do you say prop_x points to the same object as dict_x (in the heap)? Can you please explain it, may be I am missing something..
@Shaharyar There was a typo in the memory address. The answer holds good. I have taken a fresh output of the same code and updated my answer. Thanks for pointing out the typo.
Oh, I was actually sure I am being dumb to not understand it :) thanks anyway. Great example
3

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

4 Comments

I thought it might have memory of previous time, since something like def f(l=[]): keeps track between different calls to f. So the default parameters are constructed only once when the function is defined, but its local values are only constructed when it is called (and are thus different for each call) ?
@Yann: Bingo. Local values are constructed at function call time, default arguments are constructed at function definition/declaration time
About your example with @lru_cache(), I understand that it caches previous function calls, so during the second call, it sees that a first call was made with the same arguments (i.e. none), so simply return the value of the first call (which was modified in between by l[0].append(4)) ? I just want to make sure I understand.
@Yann: Again, correct. Note the arguments were the empty tuple, (), not None.
0

Variables in Python are pointers to objects. So a function would return a pointer to an object created in the function, obviating the need for copying return values.

1 Comment

I know, what I was wondering was if it returns a pointer to an object, why does it point to different things for different runs ? I thought that when a function was defined, its local variables were also defined (since it's the case for default arguments), meaning that sublist was always at the same address in the memory, but it was not the case.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.