3

Suppose I have this function

>>>a=3
>>>def num(a):
       a=5
       return a
>>>num(a)
5
>>>a
3

Value of a doesnt change.

Now consider this code :

>>> index = [1]
>>> def change(a):
       a.append(2)
       return a
>>> change(index)
>>> index
>>> [1,2] 

In this code the value of index changes. Could somebody please explain what is happening in these two codes. As per first code, the value of index shouldnt change(ie should remain index=[1]).

vinita
  • 595
  • 1
  • 9
  • 24
  • possible duplicate of [Class variables behave differently for list and int?](http://stackoverflow.com/questions/18420078/class-variables-behave-differently-for-list-and-int) (pertains to class variables, but the same mechanism is at work there). – Fred Foo Aug 24 '13 at 16:03
  • Updated my answer with id function. It might let you understand things better. – thefourtheye Aug 24 '13 at 16:10
  • 1
    relevant http://stackoverflow.com/questions/986006/python-how-do-i-pass-a-variable-by-reference – vinita Aug 24 '13 at 19:06

4 Answers4

7

You need to understand how python names work. There is a good explanation here, and you can click here for an animation of your case.

If you actually want to operate on a separate list in your function, you need to make a new one, for instance by using

a = a[:]

before anything else. Note that this will only make a new list, but the elements will still be the same.

chthonicdaemon
  • 19,180
  • 2
  • 52
  • 66
  • Hmm those would help I guess. going through them . Thanks – vinita Aug 24 '13 at 16:20
  • 1
    Another question is , that suppose I dont want my index to change even after a call to change(index). How would I edit my function change(a) to work in that case. [ Basically I want change(a) to behave exactly like num(a) ]. Is it possible ? – vinita Aug 24 '13 at 16:24
  • prepend `a.append(2)` with `a = a.copy()`. This will create a new list with contents of your `index` and assign it to `a`. This will create a __shallow copy__ of `index` - all elements that were inside `index` are shared between `index` and `a` so if index[0] is a dictionary, doing a[0]['test'] = 1 would be visible in index[0]. If you want to deep copy the list, see [deepcopy](http://docs.python.org/2/library/copy.html#copy.deepcopy) – Maciej Gol Aug 24 '13 at 16:27
  • Sorry, i've meant `list(a)`. – Maciej Gol Aug 24 '13 at 16:32
  • @vinita what does work is `b=copy(a)` (it changes the `type` of `a` but oh well). I do recommend looking at `deepcopy` and a function: `id("thingie")`. – Aleksander Lidtke Aug 24 '13 at 17:02
1

The value of index doesn't change. index still points to the same object it did before. However, the state of the object index points to has changed. That's just how mutable state works.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 1
    So you mean to say that if index=[1] resides at address 3456 in memory ,then after function call it will still reside at(or begin at) address 3456, but with an extra element at address 3457. Am I right now ? – vinita Aug 24 '13 at 15:32
  • @vinita No, you’re looking at a too low level, and your low-level description is too simple – the Python `list` object (and other objects) are more complex than that, but the low-level details are not important here. The relevant point is that the value that `index` refers to doesn’t change. The *content* of that value changes but not the value itself. – Konrad Rudolph Aug 24 '13 at 15:34
  • @vinita, speaking of pointers, think of the `num` function as `void* num(void* a)`. Explanation using C (not necessarily fully accurate): `a = 3` creates a `void* a = &(int object of 3)`. Now calling `num(a)` causes the function to receive a copied _pointer_ to `int object of 3`, not a reference to the pointer. – Maciej Gol Aug 24 '13 at 15:41
  • Im sorry but Im getting confused here :-( The last two scentences are difficult to imagine – vinita Aug 24 '13 at 15:42
  • This specific case of call-by-value, where the value is a pointer to a (mutable) object is also sometimes called call-by-oject-sharing, call-by-sharing or call-by-object. Note: this is *not* call-by-reference! – Jörg W Mittag Aug 24 '13 at 15:43
  • Can I avoid/bypass pointers to understand this concept. (Though Im sorry to have introduced it myself). I mean if you had to explain it to a beginner python programmer, who knows no more programming beyond this level. – vinita Aug 24 '13 at 15:48
  • http://pastebin.com/6Xh7cd97 I've tried to give more explanation here. This is just a way of thinking about the issue, not the real implementation. If you are used to pointers in C, it should shed some light to you. – Maciej Gol Aug 24 '13 at 15:48
1

Line 3 in the first block of code is assignment and in the second block is mutation, and that's why you are observing that behavior.

Bleeding Fingers
  • 6,993
  • 7
  • 46
  • 74
  • Hmm that kind of makes some sense to me now. So I can only mutate a global variable via a function call, but not assign it a new value. – vinita Aug 24 '13 at 15:44
  • You can assign it a new value by using `global a` statement _before_ changing it's value. Also make sure that there is no other variable named `a` in the scope you are using `global a`. – Maciej Gol Aug 24 '13 at 15:51
  • 1
    @vinita nope you are get it wrong. you can mutate any (global) variable only if it allows you to do so. The global variable in your first block of code is an `integer` which is immutable where as in the second block it's a `list` which can mutated via `append`, `extend`, etc attributes of an object of type list. – Bleeding Fingers Aug 26 '13 at 04:07
0

The issue you are encountering is:

a = 3
def num(a):
    # `a` is a reference to the argument passed, here 3.
    a = 5
    # Changed the reference to point at 5, and return the reference.
    return a

num(a)

The a in the num function is a diffrent object than the a defined globally. It works in case of the list because a points at the list passed, and you modify the object being referenced to by the variable, not the reference variable itself.

Maciej Gol
  • 15,394
  • 4
  • 33
  • 51