elegant way to pass operator to attribute in python

Viewed 71

Basically my Question: Is there an elegant way, if I have a given Container class

class Container:
    def __init__(self, value):
        self.value = value

to pass any operator to the value parameter of the Container class?

So one obvious solution would be to override any single operator on its own. So for example for + I could do:

class Container:
    def __init__(self, value):
        self.value = value
    def __add__(self, other):
        return self.value + other

so that for example

c1 = Container(1)
c1 += 1
print(c1)

would result in

2

and

c2 = Container("ab")
c2 += "c"
print(c2)

would result in

abc

So what I could do is to override all arithmetic built-in functions from python. But my Question is, is there a more elegant (shorter) way to do this for any operator?

2 Answers

You can factor out some of the more repetitive boilerplate:

import operator


class Container:
    def __init__(self, value):
        self.value = value

    def _generic_op(f):
        def _(self, other):
            return f(self.value, other)
        return _

    __add__ = _generic_op(operator.add)
    __sub__ = _generic_op(operator.sub)
    __mul__ = _generic_op(operator.mul)
    # etc

    # No need for _generic_op as a class attribute
    del _generic_op

If the number of overloads are smaller than the different classes you want to overload in, you can define saperate class for each overload and inherit it. So,

class Adder:
    def __add__(self, val):
        self.value += val
        return self

class Subtractor:
    def __sub__(self, val):
        self.value -= val
        return self

class Foo(Adder, Subtractor):
    def __init__(self, value):
        self.value = value

foo = Foo(5)
foo -= 3
foo += 7
print(foo.value)

But I am against doing such operations between custom DataTypes and native dataTypes. Consider this just a simple example that doesn't follow good coding principles in that sense.
If incase you still want to allow adding int/float directly, you might also want to handle Foo class objects inside too.

Related