7

I'm going through some Python activities and was given example code with this operator: y *= -1

I had a look through the relevant Python docs, to no avail.

I know y += 1, for example, is short for y = y + 1. So is this y = y * -1 y equals y times -1 maybe?

Closest thing in Python docs I could find is this: x * y: product of x and y

Is this it?

BenniMcBeno
  • 2,415
  • 7
  • 24
  • 28

2 Answers2

12

In the vast majority of the cases

y *= <expr>

is the same as

y = y * <expr>

but in the general case, it is interpreted as:

y = imul(y, <expr>)

which is then equivalent to:

y = y.__imul__(<expr>)

if y's type overrides __imul__.

This means that if y's type overrides the inplace multiplication operator, y*=<expr> is performed inplace, while y=y*<expr> is not.


EDIT

It might not be immediately clear why the assignment is needed, i.e. why it is intrepreted as y = imul(y, <expr>), and not just imul(y, <expr>).

The reason is that it makes a lot of sense for the following two scenarios to give the same result:

c = a * b

and

c = a
c *= b

Now, this of course works if a and b are of the same type (e.g. floats, numpy arrays, etc.), but if they aren't, it is possible for the result of the operation to have the type of b, in which case the operation cannot be an inplace operation of a, thus the result needs to be assigned to a, in order to achieve the correct behavior.

For example, this works, thanks to the assignment:

from numpy import arange
a = 2
a *= arange(3)
a
=> array([0, 2, 4])

Whereas if the assignment is dropped, a remains unchanged:

a = 2
imul(a, arange(3))
=> array([0, 2, 4])
a
=> 2
shx2
  • 61,779
  • 13
  • 130
  • 153
  • Can you explain why is it interpreted as `y = y.__imul__()` and not just `y.__imul__()`? – wim May 22 '13 at 05:09
  • See [the docs](http://docs.python.org/3.3/reference/datamodel.html#object.__iadd__): " These methods should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self)." – shx2 May 22 '13 at 05:16
  • 2
    @wim there's then the infamous case of tuple modification `x = ([],)`, `x[0] += ['a']` `x` – jamylak May 22 '13 at 05:17
  • @jamylak, yes! one of my favorites :) – shx2 May 22 '13 at 05:18
  • Yep, I asked a question about that thing over a year ago... http://stackoverflow.com/q/9172263/674039 – wim May 22 '13 at 05:22
  • @shx2 I'm aware of that, which is a documentation if not explanation of why `__imul__` returns self (or something self-like). But it does not explain why the assignment is done. Why wouldn't `y.__imul__()` work equally well as an interpretation here? – wim May 22 '13 at 05:26
  • 1
    Interesting. Perhaps the AST greatly reflects the x86 and other integer multiplications machine instructions for performance (mul instruction has implicit source operand)? – chaz May 22 '13 at 08:38
  • I don't think that is a good example, because `int` doesn't define `__imul__` at all - the first case only works because `*=` falls back on `__mul__`. So, this has nothing to do with whether `ndarray.__imul__` is implemented this way or the other. – wim Feb 25 '14 at 19:54
  • Note the case where `a` and `b` are ndarrays of different shape. Then `c = a * b` works but `c = a; c *= b` fails – wim Feb 25 '14 at 20:12
3

Yes that's correct. It just means multiply the left-hand value by negative the right-hand value. They're both arithmetic operators that differ simply by operation and expression binding, so I believe +/* are parallel everywhere else in overloads.

y = y * -1
chaz
  • 568
  • 1
  • 8
  • 22
  • 3
    It's not 100% identical, if `y` has side effects when evaluated (such as printing or modifying state). In `y *= -1`, `y` is evaluated once. In `y = y * -1`, `y` is is evaluated twice. – Patashu May 22 '13 at 04:55
  • You mean the second evaluated from the equal operator? Nvm, you said modifying state. It's more apparent when dealing with items: `k[key] *= -1` – chaz May 22 '13 at 04:57