0

This appeared as some test question. If you consider this function which uses a cache argument as the 1st argument

def f(cache, key, val): 
    cache[key] = val
    # insert some insanely complicated operation on the cache
    print cache

and now create a dictionary and use the function like so:

c = {}
f(c,"one",1)
f(c,"two",2)

this seems to work as expected (i.e adding to the c dictionary), but is it actually passing that reference or is it doing some inefficient copy ?

Peter Moore
  • 1,632
  • 1
  • 17
  • 31
  • 2
    possible duplicate of [Python: How do I pass a variable by reference?](http://stackoverflow.com/questions/986006/python-how-do-i-pass-a-variable-by-reference) – rid May 08 '12 at 06:19
  • not exactly sure what you mean but python doesn't make copies unless you explicitly tell it to by using the `copy` module. – jamylak May 08 '12 at 06:19
  • Is your question about whether the integer values that you are passing to f are passed by reference or by value? – Andrew Gorcester May 08 '12 at 06:19
  • @AndrewG. There has been several threads where pass by reference dosent happen in python like other languages (as in a pointer to an object). This is what spurred me to ask the question. – Peter Moore May 08 '12 at 06:27
  • @PeterMoore What is your question actually about? The dictionary, the values or the keys? – jamylak May 08 '12 at 06:30
  • 1
    @PeterMoore I think the accepted answer on that question listed above will answer the question I think you are asking. If it doesn't, then please specify what the confusing part is. I think the cache bit is a red herring. – Andrew Gorcester May 08 '12 at 06:32
  • @AndrewG Yes ive seen the thread that is that is indicated. I have also seen others that say more about the subject. My question is that in the passing of the dictionary to the function is there some big copy and then reassignment happening on exit. I suppose i can test this by using a global to see if using a global (which should be static) is more efficient than using a reference. They should both be about the same. – Peter Moore May 08 '12 at 06:40
  • @PeterMoore There is no big copy and reassignment. The parameter `cache` is bound to the value of `c`. – jamylak May 08 '12 at 06:42
  • 1
    All arguments in Python are passed by reference, so, yes, `c` is passed by reference. – kindall May 08 '12 at 06:44
  • "Passed by reference" implies that `c` can be rebound inside the function and the rebinding will affect the contents of `c`. It's technically more of a "pass of a reference by value". It doesn't really fit into the reference/value dichotomy very well. – Andrew Gorcester May 08 '12 at 06:49
  • It's not the passing by reference that is different in Python from other languages; it's assignment, which is always by reference as well. – kindall May 08 '12 at 15:21
  • @kindall as a matter of interest and i know this from C; a reference is a memory pointer with a type wrapper. If we are passing a reference to an item on the heap, and reassign that reference to another heap object the outer scope will see the new object. Is "pass of a reference by value" saying that we are making a copy of the reference which itself gets destroyed on exit ? – Peter Moore May 08 '12 at 16:45
  • If you try to apply C semantics to Python you're going to get lost. If you do `a = 1` then `a` does not *contain* 1, `a` is a pointer to an integer object 1 stored elsewhere. If you then reassign `a = 2`, you don`t change the value of the integer object 1 to 2, you are merely storing a different pointer in `a`. Since everything is an object, every variable contains a pointer to some object, and every function call passes those pointers. The difference is that assignment just changes the pointers; a Python assignment never "follows the pointer" to modify the object pointed to. – kindall May 08 '12 at 17:43
  • @kindall I get the a=1 thing. The phrase "the assignment never follows the pointer" dosnt seem true when you consider mutable dict obj in function f() above. It does resolve to the dictionary object with the label "c". Im trying to understand the statement "pass of a ref by value". I take it to mean when we pass c to f() we copy the object which is a ref to the c.dict to f() and call it cache. Now we have obj c, cache and c.dict. c & cache point to c.dict. In f() we can change cache.dict because pointhing to c.dict. If we change cache to another dict the outer scope cant see. Is this true? – Peter Moore May 08 '12 at 22:09
  • Ah, but `cache[key] = value` is not an assignment; it's actually a method call to `cache.__setitem__()`. `cache` is not modified by this statement. – kindall May 08 '12 at 22:11
  • @kindall i understand that which is why that works. I asked "If we change cache to another dict" i.e if we said cache = {} then the cache ref now points to anonymous.dict (or even some global dict outside the function) rather than c.dict. – Peter Moore May 08 '12 at 22:33
  • Oh, yeah, I see what you're saying. Right, `cache = {}` would merely point `cache` to a different dict than the one that is passed in. That's what I meant by *assignment,* not argument-passing, being where the difference is. – kindall May 08 '12 at 22:39

1 Answers1

1

The dictionary passed to cache is not copied. As long as the cache variable is not rebound inside the function, it stays the same object, and modifications to the dictionary it refers to will affect the dictionary outside.

There is not even any need to return cache in this case (and indeed the sample code does not).

It might be better if f was a method on a dictionary-like object, to make this more conceptually clear.

If you use the id() function (built-in, does not need to be imported) you can get a unique identifier for any object. You can use that to confirm that you are really and truly dealing with the same object and not any sort of copy.

Andrew Gorcester
  • 19,595
  • 7
  • 57
  • 73
  • i see. yep i tested it and found that using a global to be a little faster (as expected) but only be 1 thenth of a second on 10 million iterations. Thanks for the reassurance. – Peter Moore May 08 '12 at 07:01