I’m writing a class overloading the list type.
I just wrote this and I’m wondering if there exists any other way less redundant to do it :
class Vector:
def __mul__(self, other):
#Vector([1, 2, 3]) * 5 => Vector([5, 10, 15])
if isinstance(other, int) or isinstance(other, float):
tmp = list()
for i in self.l:
tmp.append(i * other)
return Vector(tmp)
raise VectorException("We can only mul a Vector by a scalar")
def __truediv__(self, other):
#Vector([1, 2, 3]) / 5 => Vector([0.2, 0.4, 0.6])
if isinstance(other, int) or isinstance(other, float):
tmp = list()
for i in self.l:
tmp.append(i / other)
return Vector(tmp)
raise VectorException("We can only div a Vector by a Scalar")
def __floordiv__(self, other):
#Vector([1, 2, 3]) // 2 => Vector([0, 1, 1])
if isinstance(other, int) or isinstance(other, float):
tmp = list()
for i in self.l:
tmp.append(i // other)
return Vector(tmp)
raise VectorException("We can only div a Vector by a Scalar")
As you can see, every overloaded method is a copy/paste of the previous with just small changes.
Solution:
What you want to do here is dynamically generate the methods. There are multiple ways to do this, from going super-dynamic and creating them on the fly in a metaclass’s __getattribute__ (although that doesn’t work for some special methods—see the docs)
to generating source text to save in a .py file that you can then import. But the simplest solution is to create them in the class definition, something like this:
def _make_op_method(op):
def _op(self, other):
if isinstance(other, int) or isinstance(other, float):
tmp = list()
for i in self.l:
tmp.append(op(i. other))
return Vector(tmp)
raise VectorException("We can only {} a Vector by a scalar".format(
op.__name__.strip('_'))
_op.__name__ = op.__name__
return _op
__mul__ = _make_op(operator.__mul__)
__truediv__ = _make_op(operator.__truediv__)
# and so on
You can get fancier and set _op.__doc__ to an appropriate docstring that you generate (see functools.wraps in the stdlib for some relevant code), and build __rmul__ and __imul__ the same way you build __mul__, and so on. And you can write a metaclass, class decorator, or function generator that wraps up some of the details if you’re going to be doing many variations of the same thing. But this is the basic idea.
The operator.mul, etc., come from the operator module in the stdlib—they’re just trivial functions where operator.__mul__(x, y) basically just calls x * y, and so on, made for when you need to pass around an operator expression as a function.
There are some examples of this kind of code in the stdlib—although far more examples of the related but much simpler __rmul__ = __mul__.
The key here is that there’s no difference between names you create with def and names you create by assigning with =. Either way, __mul__ becomes an attribute of the class, and its value is a function that does what you want.
If you don’t understand how that works, you probably shouldn’t be doing this, and should settle for Ramazan Polat’s answer. It’s not quite as compact, or as efficient, but it’s surely easier to understand.