In Python, all parameters are passed by reference. However, that doesn't quite mean the same thing as it does in a language like C++. For example, certain built-in types, such as int, are immutable. Let me give an example.
x = 5
Right now, x is a reference to an object. That object is an int and holds the value 5. However, since ints are immutable,
x += 1
doesn't change the object to which x refers from a 5 to a 6. Instead, x is changed to refer to a new object which holds the value 6.
So, when, in __mul__, you do self.w *= q0, that doesn't change the underlying int that self.w and self.q[0] used to refer to. self.w now refers to a different int and self.q[0] remains unchanged.
In addition to the mutable/immutable distinction, there's another way in which Python references differ from, say, C++ references. Reassignment, depending on how it's done, can change the referent object. For example,
x = [1, 2, 3]
y = x # x and y now refer to the same object
y.append(4)
print(x) # [1, 2, 3, 4], because x is y
x = [5, 6, 7] # x now refers to a new object
print(y) # [1, 2, 3, 4]
With in-place arithmetic reassignment, such as +=, it goes back to the mutable/immutable issue. For example, since ints are immutable, they can't be changed in place and so the referent has to change. However, for lists,
x = [1, 2, 3]
y = x
x += [4]
print(y) # [1, 2, 3, 4]
This is because lists have an __iadd__ method which alters the list in place.
For something to chew on, consider these examples:
def add_one(x):
x += 1
def append_one(x):
x.append(1)
def add_one_list(x):
x += [1]
def add_one_list_part_2(x):
x = x + [1]
x = 5
add_one(x)
print(x) # What will be printed?
y = [4, 3, 2]
append_one(y)
add_one_list(y)
add_one_list_part_2(y)
print(y) # What will be printed?
__init__from within__mul__to set the new values.