0

When I have a function like

def foo(A):
    tmp=A
    tmp=tmp+1
    *rest of the function*
    return

or

def foo(A):
    global tmp
    tmp=A
    tmp=tmp+1
    *rest of the function*
    return

both to "A" and "tmp" 1 is added instead of only to "tmp". What am I doing wrong and how can I fix it?

Erik
  • 155
  • 2
  • 10
  • I'm guessing based on the question that you tried hard to simplify the code to make your question clear, but ended up simplifying the underlying issue out of it. If you do simplify your code to try to make your question more brief, be sure to test it first! :) – Mark R. Wilkins Aug 28 '13 at 00:18
  • I actually had reduced the problem far enough that I could safely reduce the code to this size, but by accident I confused two versions of my code. Both contained the same error yet both on a different position. The combination of the two is, as you've correctly concluded, to far simplified. Fortunately the solution to the problem was given despite of this. – Erik Aug 30 '13 at 22:07
  • No problem, glad I was able to help in the end despite my initial confusion! – Mark R. Wilkins Aug 30 '13 at 22:33

2 Answers2

0

As nneonneo's comment under Blake's answer (which describes a significant part of the problem) states, the code in the original post doesn't actually do what he states. For example:

def foo(A):
   tmp = A
   tmp = tmp + 1
   return (A, tmp)

foo(3)

returns (3,4), meaning A has been left unchanged.

Where this would not be true is where A is a mutable type. Mutable types include lists, dictionaries, and derived types of those, while integers, floats, and tuples are not mutable.

For example:

def foo(A):
   tmp = A
   tmp[0] = tmp[0] + 1
   return (A, tmp)

foo([1, 2])

which returns ([2, 2], [2, 2]), and in this case A has changed.

That foo changes the value of A in the list case but not the integer case is because lists are mutable and integers are not. Assigning A to tmp when A is a mutable type assigns a reference to the mutable object, and changing one of its elements (as in tmp[0] = tmp[0] + 1) doesn't make a new object.

If you do not want your function to have this side-effect-like behavior for a list, for example, a common Python idiom is to use slice notation to duplicate the list. This will make a new list object when you assign it to tmp that is a copy of the list object in A:

def foo(A):
   tmp = A[:]
   # this slice makes a new list, a copy of A

   tmp[0] = tmp[0] + 1
   return (A, tmp)

foo([1, 2])

This returns ([1, 2], [2, 2]), so A is unchanged and tmp is changed.

There are other ways to copy lists or other mutable objects which are subtly different from each other. How to clone or copy a list? has a great description of your choices.

Community
  • 1
  • 1
Mark R. Wilkins
  • 1,282
  • 7
  • 15
-1

That's because python method parameters are pass by reference, not pass by value. You're essentially modifying the same place in memory, that two different variables point to.

>>> def foo(a):
    tmp = a
    print(tmp, a, id(a), id(tmp))
>>> foo(5)
5 5 505910928 505910928
>>> b = 5
>>> foo(b)
5 5 505910928 505910928
>>> id(b)
505910928

And with the global example:

>>> def foo(a):
    global tmp
    a = tmp
    print(a, tmp, id(a), id(tmp))
>>> foo(5)
7 7 505910960 505910960
>>> foo('s')
7 7 505910960 505910960
>>> tmp
7
>>> tmp = 6
>>> foo('a')
6 6 505910944 505910944
blakev
  • 4,154
  • 2
  • 32
  • 52
  • Yes, but `tmp = tmp + 1` should rebind `tmp`. – nneonneo Aug 27 '13 at 23:56
  • I don't think his example code is what he's actually doing. tmp will be rebound because you're giving it a new integer, which will be a new place in memory. If he assigns the local variable A to a global variable tmp, it wouldn't matter what A is because tmp needs to be explicitly changed. These memory pointer complications would be more evident if his sample wasn't an integer. And, like I said, I don't think he was using ints when he came accross the problem (because he wouldn't have ever seen it) – blakev Aug 28 '13 at 00:00
  • 1
    You provided a nice description of an important part of the OP's issue, but I think the difference between mutable and immutable types was another important basic concept that he was probably missing. I edited my answer to make sure to give you credit for providing a key piece of the puzzle. :) – Mark R. Wilkins Aug 28 '13 at 00:27
  • 1
    Python is fully pass-by-value only. Consider a variable (name) that is bound to a list. It is *impossible* to make that variable (name) be bound to a different list (in the calling scope) by passing it into a function; IOW you cannot pass the reference, only the value itself (the list). – Caleb Hattingh Aug 28 '13 at 00:36
  • The truth is that "pass by reference" and "pass by value," which are meaningful in a context like C, are poor analogues for the Python concepts. Everything passed into a function in Python is implemented as a pointer to an object. "Mutable" objects can have their contents changed and "immutable" objects cannot. The calling function's object pointer, however, will not change, though the contents of the object itself may (if it is mutable.) Whether passing a pointer to a thing is passing the thing as reference or passing the pointer by value is a needless semantic argument here. – Mark R. Wilkins Aug 28 '13 at 00:42
  • No disagreements from me. My comment was actually for OP. – Caleb Hattingh Aug 28 '13 at 01:01