Question

I'm trying to implement a class with a method that calls another method with an object that's part of the class where the lowest method mutates the object. My implementation is a little more complicated, so I'll post just some dummy code so you can see what I'm talking about:

class test:
    def __init__(self,list):
        self.obj = list
    def mult(self, x, n):
        x = x*n
    def numtimes(self, n):
        self.mult(self.obj, n)

Now, if I create an object of this type and run the numtimes method, it won't update self.obj:

m = test([1,2,3,4])
m.numtimes(3)
m.obj  #returns [1,2,3,4]

Whereas I'd like it to give me [1,2,3,4,1,2,3,4,1,2,3,4]

Basically, I need to pass self.obj to the mult method and have it mutate self.obj so that when I call m.obj, I'll get [1,2,3,4,1,2,3,4,1,2,3,4] instead of [1,2,3,4].

I feel like this is just a matter of understanding how python passes objects as arguments to methods (like it's making a copy of the object, and instead I need to use a pointer), but maybe not. I'm new to python and could really use some help here.

Thanks in advance!!

Was it helpful?

Solution

Allow me to take on the bigger subject of mutability.

Lists are mutable objects, and support both mutable operations, and immutable operations. That means, operations that change the list in-place, and operations that return a new list. Tuples, for contrast, only are only immutable.

So, to multiply a list, you can choose two methods:

  1. a *= b

This is a mutable operation, that will change 'a' in-place.

  1. a = a * b

This is an immutable operation. It will evaluate 'a*b', create a new list with the correct value, and assign 'a' to that new list.

Here, already, lies a solution to your problem. But, I suggest you read on a bit. When you pass around lists (and other objects) as parameters, you are only passing a new reference, or "pointer" to that same list. So running mutable operations on that list will also change the one that you passed. The result might be a very subtle bug, when you write:

>>> my_list = [1,2,3]
>>> t = test(my_list)
>>> t.numtimes(2)
>>> my_list
[1,2,3,1,2,3]  # Not what you intended, probably!

So here's my final recommendation. You can choose to use mutable operations, that's fine. But then create a new copy from your arguments, as such:

def __init__(self,l):
    self.obj = list(l)

OR use immutable operations, and reassign them to self:

def mult(self, x, n):
    self.x = x*n

Or do both, there's no harm in being extra safe :)

OTHER TIPS

The multiplication x * n creates a new instance and does not alter the existing list. See here:

a = [1]
print (id (a) )
a = a * 2
print (id (a) )

This should work:

class test:
    def __init__(self,list):
        self.obj = list

    def mult(_, x, n):
        x *= n

    def numtimes(self, n):
        self.mult(self.obj, n)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top