5

For example in this code:

def product(list):
    p =1
    for i in list:
        p *= i
    return p

I found this code, but I need to be able to explain each and every part of it.

georg
  • 211,518
  • 52
  • 313
  • 390
  • 1
    Naming a variable `list` is a bad coding practice--it overrides the built-in. –  Oct 21 '13 at 14:03

5 Answers5

9

It's shorthand for

p = p * i

It's analogous to the more frequently encountered p += i

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • 4
    That's true for this particular code chunk, but in general this is a bit of over-simplification. – georg Oct 21 '13 at 12:04
5

Taken from the first result in google:

Multiply AND assignment operator, It multiplies right operand with the left operand and assign the result to left operand

*= is the same as saying p = p * i.

This link contains a list of all the operators in their various, wonderful combinations.

Example

A pseudo-code explanation of your code is as follows:

assume list := {4, 6, 5, 3, 5, 1}
p := 1.

for(each number in list)
    p = the current value of p * the current number.
    // So: 1 * 4, 4 * 6, 24 * 5, 120 * 3...

return p. 
christopher
  • 26,815
  • 5
  • 55
  • 89
4

It's not exactly the same as p = p * i:

>>> class A(int):
...     def __imul__(self, item):
...         print '__imul__ is running!'
...     
...     def __mul__(self, item):
...         print '__mul__ is running!'
>>> mynumber = A(10)
>>> mynumber *= 5
__imul__ is running!
>>> mynumber = A(10)
>>> mynumber * 5
__mul__ is running!

However the output is mostly the same, so you should probably treat it so

TerryA
  • 58,805
  • 11
  • 114
  • 143
4

The idea behind the operator a *= b is to mean the same as a = a * b. In most cases (as in yours) it will do exactly this, so it multiplies a variable with a value and stores the result in the variable again.

The notation using *= might be faster (depending on the classes involved), and it is, in any case, the clearer version, so it should be favored. Its main advantage shows if the variable a is itself already a complex expression like myGiantValueField[f(42)].getValue()[3]:

myGiantValueField[f(42)].getValue()[3] = myGiantValueField[f(42)].getValue()[3] * 3

is certainly less readable and due to code doubling more prone to fixing-errors than

myGiantValueField[f(42)].getValue()[3] *= 3

In general, though, the operator *= calls the method __imul__() of the variable a and hands over the argument b, so it means exactly the same as a.__imul__(b) (which isn't as intuitive).

Why could there be a difference between a = a * b and a *= b? Three reasons come two mind at once, but there might be more.

  1. A class could implement only the *= operator (so a * b could be undefined although a *= b exists) due to performance aspects. Multiplying a very large value (e. g. a giant matrix) with a number is sometimes better done in-place to avoid having to allocate memory for the result (which might at once after computation be copied into the original variable by the assignment). That a = a * b is internally sth like tmp = a * b and a = tmp.
  2. A class might find its use unintuitive using the direct multiplication, hence it could leave the * operator unimplemented. An example might be a class representing the volume of the computer speaker. Doubling the volume might make sense (volumeKnob *= 2) whereas computing it without using (assigning) it is not recommended (x = volumeKnob * 2? ← makes no sense as it does nothing).
  3. If the type of the result of the multiplication differs from the type of a, I would not expect or recommend implementing the *= operator as it would be misleading. An example might be if a and b are vectors whose multiplication result would be a matrix (or a number). The name __imul__ already suggests that it is meant for being applied iteratively, i. e. more than once. But if a's type changed, this would be problematic.
Alfe
  • 56,346
  • 20
  • 107
  • 159
4

Usually p *= i is the same as p = p * i.

Sometimes it can be different, and I think the explanations already posted aren't clear enough for that, so:

It can be different when p is a mutable object. In that case the in-place *= may modify the original object instead of creating a new one. Compare what happens to q in each of these:

>>> p = q = [2]
>>> p *= 5
>>> p
[2, 2, 2, 2, 2]
>>> q
[2, 2, 2, 2, 2]

>>> p = q = [2]
>>> p = p * 5
>>> p
[2, 2, 2, 2, 2]
>>> q
[2]

If can also be different when p is a complex expression with side effects as the in-place version only evaluates sub-expressions once. So for example:

aList[randint(0, 5)] *= 3

is not the same as:

aList[randint(0, 5)] = aList[randint(0, 5)] * 3
Duncan
  • 92,073
  • 11
  • 122
  • 156