saurav
saurav

Reputation: 424

Why does one function work and other don't ? How is the mutability or immutability working here?

Suppose I have a list as such li = [1, 2, 3, 4, 5] and a scale function as

def scale(data, factor):
    for j in range(len(data)):
        data[j] *= factor

Now if I pass li to scale function with a factor = 2, my list gets modified. That is the actual parameter gets changed here. So if i print li after executing the above function it gives [2, 4, 6, 8, 10] and not the original one.

Another way scale function is implemented is as follows:-

def scale(data, factor):
    for val in data:
        val *= factor

Now if I execute scale(li, 2) then it doesn't modify the actual parameter. So li stays as [1, 2, 3, 4, 5]. So my question is why does one modify the list and the other doesn't? Has this got anything to do with mutability or immutability?

Upvotes: 4

Views: 108

Answers (4)

warvariuc
warvariuc

Reputation: 59594

data[j] *= factor is the same as data[j] = data[j] * factor so you are modifying data object. This operation calls __setitem__ method of list data, which puts the result of data[j] * factor into the corresponding position.

When you do for val in data: in val is stored a reference to an item in data. That item (object) doesn't not know anything about the list data it was taken from. You are now working only with the item itself, not the list which contains another reference to it (along with val).

val *= factor creates a new object (the result of the product) and puts a reference to it into variable val. data still contains a reference to the old object/value which was in val before the assignment.

See more here: https://stackoverflow.com/a/8140747/248296

Upvotes: 1

Leon
Leon

Reputation: 3036

The main difference is the fact, that you use an index to access the list in the first example and store the result in the list directly. In the second example you store the result in a variable which is holding the value taken from the list.

You can think of for val in data: as a shorthand for:

for i in range(len(data)):
    val = data[i]

Now changing val (through val *= factor) will not change data (Why should it?). And as this is, what your second example is doing, the data list is not modified.

On the other hand, when you do data[i] *= factor, you are actually modifying the list, because you store the result in data[i] instead of the "temporary" variable val.

You could simplify it further, and look at

data[0] = 1

and compare it to

val = data[0]
val = 1

It should be obvious why one of them changes data and one does not. Your loops do effectively the same things and therefor one changes data and one does not.

Upvotes: 1

Olivier Melançon
Olivier Melançon

Reputation: 22294

The inplace operator...

x *= y    

...is more or less equivalent to...

x = x * y

In particular, the result will be reassigned to x.

In your first example, you assign to data[j], index access changes the value at index j. This mutates your list.

In your second example, the values from the list are assigned to val, the inline operator thus reassignes to val. This is not an index access, so the list is not mutated.

In other words, your second example behaves like this:

for i in range(len(data)):
    # values from the list are assigned to val
    val = data[i]

    # A new value is assigned to val
    val = value * factor

Upvotes: 0

blue note
blue note

Reputation: 29071

In the second example, the for loop creates a variable val=1 (and so on). When you do val = val * factor, the value of val changes. However, this has no connection to the original list[0]. You are just changing a temporary variable val

On the first example however, you do list[0] = list[0] * factor, so the assignment is done to the list itself.

I wouldn't say that it's so much a matter of mutability/immutability here (the right hand of both assignments is the same, and multiplies an immutable object in both cases). Mutability would be an issue if, eg, in the second example, you were passing val instead of data to your function, and change its value. In that case, the original value would not change, because val would be immutable.

Upvotes: 1

Related Questions