73

Say I have a list l. Under what circumstance is l.__rmul__(self, other) called?

I basically understood the documentation, but I would also like to see an example to clarify its usages beyond any doubt.

nbro
  • 15,395
  • 32
  • 113
  • 196
porgarmingduod
  • 7,668
  • 10
  • 50
  • 83

3 Answers3

164

When Python attempts to multiply two objects, it first tries to call the left object's __mul__() method. If the left object doesn't have a __mul__() method (or the method returns NotImplemented, indicating it doesn't work with the right operand in question), then Python wants to know if the right object can do the multiplication. If the right operand is the same type as the left, Python knows it can't, because if the left object can't do it, another object of the same type certainly can't either.

If the two objects are different types, though, Python figures it's worth a shot. However, it needs some way to tell the right object that it is the right object in the operation, in case the operation is not commutative. (Multiplication is, of course, but not all operators are, and in any case * is not always used for multiplication!) So it calls __rmul__() instead of __mul__().

As an example, consider the following two statements:

print "nom" * 3
print 3 * "nom"

In the first case, Python calls the string's __mul__() method. The string knows how to multiply itself by an integer, so all is well. In the second case, the integer does not know how to multiply itself by a string, so its __mul__() returns NotImplemented and the string's __rmul__() is called. It knows what to do, and you get the same result as the first case.

Now we can see that __rmul__() allows all of the string's special multiplication behavior to be contained in the str class, such that other types (such as integers) do not need to know anything about strings to be able to multiply by them. A hundred years from now (assuming Python is still in use) you will be able to define a new type that can be multiplied by an integer in either order, even though the int class has known nothing of it for more than a century.

By the way, the string class's __mul__() has a bug in some versions of Python. If it doesn't know how to multiply itself by an object, it raises a TypeError instead of returning NotImplemented. That means you can't multiply a string by a user-defined type even if the user-defined type has an __rmul__() method, because the string never lets it have a chance. The user-defined type has to go first (e.g. Foo() * 'bar' instead of 'bar' * Foo()) so its __mul__() is called. They seem to have fixed this in Python 2.7 (I tested it in Python 3.2 also), but Python 2.6.6 has the bug.

Mat
  • 1,440
  • 1
  • 18
  • 38
kindall
  • 178,883
  • 35
  • 278
  • 309
  • 2
    kindall said: This is probably wasted effort now that you've accepted an answer, but: I want to assure you that your effort was not wasted - I had a problem with enabling __rmul__ use in Vector algebra (for scalar multiplication of vectors). Your explanation was enough to convince me that in case of (scalar) * (vector) operation the __mul__ method should end with either "raise NotImplementedError()" or "return Not Implemented" to enable a call to go to the __rmul__ method. Thank you for your help! – user377367 May 07 '11 at 01:13
  • 4
    Actually, even multiplication itself is not always commutative once you start thinking about more general mathematical structures. Consider matrix multiplication, for instance. – Shaun Harker Dec 01 '16 at 12:05
  • 6
    The first sentence of this answer is not strictly correct. When the right hand-side object is an instance of a subclass of the left-hand-side object's type, then the right-hand-side object will get the first chance to handle the operation. – wim Aug 09 '19 at 19:49
12

Binary operators by their nature have two operands. Each operand may be on either the left or the right side of an operator. When you overload an operator for some type, you can specify for which side of the operator the overloading is done. This is useful when invoking the operator on two operands of different types. Here's an example:

class Foo(object):
    def __init__(self, val):
        self.val = val

    def __str__(self):
        return "Foo [%s]" % self.val


class Bar(object):
    def __init__(self, val):
        self.val = val

    def __rmul__(self, other):
        return Bar(self.val * other.val)

    def __str__(self):
        return "Bar [%s]" % self.val


f = Foo(4)
b = Bar(6)

obj = f * b    # Bar [24]
obj2 = b * f   # ERROR

Here, obj will be a Bar with val = 24, but the assignment to obj2 generates an error because Bar has no __mul__ and Foo has no __rmul__.

I hope this is clear enough.

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
0

__mul__() is the case of a dot product and the result of the dot product should be a scalar or just a number, i.e. __mul__() results in a dot product multiplication like x1*x2+y1*y2. In __rmul__() , the result is a point with x = x1*x2 and y = y1*y2 .