3

I built a matrices calculator and I want to make one mul method for multiplication by scalar And another one for multiplication by other matrix. I have an if- else block but I prefer it to be in two different methods but i want both of them to work with the * operator . How should i do it?

  • 1
    Use `__mul__` (`*`) and `__matmul__` (`@`). – askaroni Apr 01 '20 at 23:38
  • `functools.singledispatch` comes to mind... – Todd Apr 01 '20 at 23:40
  • Hi and welcome to StackOverflow! You are asking about `__mul__` here but maybe this [similar question](https://stackoverflow.com/q/57829402/1723886) about `__add__` will help you. Though sadly it seems that the solutions there won't help you define two different methods. – Alex Telon Apr 01 '20 at 23:41

2 Answers2

4

You can use __mul__ (*) and __matmul__ (@) or, if you want to use just one operator without an if, use singledispatchmethod (Python 3.8+).

import functools

class Matrix:
    @functools.singledispatchmethod
    def __mul__(self, other):
        raise NotImplementedError(other)

    @__mul__.register
    def _(self, other: int):
        print('int')

    @__mul__.register
    def _(self, other: str):
        print('str')
Matrix() * 1 # 'int'
Matrix() * 'a' # 'str'
askaroni
  • 913
  • 5
  • 10
1

functools.singledispatchmethod seems like a fit for what you're describing. The other example uses parameter annotation to specify the types, or they can be specified as parameters to the decorators:

>>> class Foo:
...     def __init__(self, value):
...         self._value = value
... 
...     @functools.singledispatchmethod
...     def __mul__(self, other):
...         print("hmm...")
... 
...     @__mul__.register(int)
...     def _(self, other):
...         print("Using the int version.")
...         return self._value * other
... 
...     @__mul__.register(list)
...     def _(self, other):
...         print("Using the list version.")
...         return [item * self._value for item in other]
...         
>>> f = Foo(8)
>>> f * 3
Using the int version.
24
>>> f * [1, 2, 3]
Using the list version.
[8, 16, 24]

If using a Python version earlier than 3.8 - (probably better to just do the type checking within a single method without using decorators in this case):

>>> class Foo:
...     def __init__(self, value):
...         self._value = value
...         self.mul = functools.singledispatch(self.mul)
...         self.mul.register(int, self.mul_int)
...         self.mul.register(list, self.mul_list)
... 
...     def __mul__(self, other):
...         return self.mul(other)
... 
...     def mul(self, other):
...         print("Default mul() called.")
... 
...     def mul_int(self, other):
...         print("Using the int version.")
...         return self._value * other
... 
...     def mul_list(self, other):
...         print("Using the list version.")
...         return [item * self._value for item in other]
...         
>>> f = Foo(3)
>>> f * 7
Using the int version.
21
>>> f * [1, 2, 3]
Using the list version.
[3, 6, 9]
>>> 
Todd
  • 4,669
  • 1
  • 22
  • 30