Generic binary operation in a class definition?
<p>I am writing a tiny linear algebra module in Python 3, and there are a number of binary operators to define. Since each definition of a binary operator is essentially the same with only the operator itself changed, I would like to save some work by writing a generic binary operator definition only once.</p>

<p>For example:</p>

<pre><code>class Vector(tuple):
    def __new__(self, x):
        super().__new__(x)

    # Binary operators

    def __add__(self, xs):
        try:
            return Vector(a + x for a, x in zip(self, xs)))
        except:
            return Vector(a + x for a in self)

    def __and__(self, xs):
        try:
            return Vector(a &amp; x for a, x in zip(self, xs))
        except:
            return Vector(a &amp; x for a in self)

    ... # mul, div, or, sub, and all other binary operations
</code></pre>

<p>The binary operators above all have the same form. Only the operator is changed. I wonder if I could instead write all operators at once, something like this:</p>

<pre><code>def __bop__(self, xs):
    bop = get_bop_somehow()
    try:
        return Vector(bop(a, x) for a, x in zip(self, xs)))
    except:
        return Vector(bop(a, x) for a in self)
</code></pre>

<p>I've heard that Python can do magical things with the <code>__getattr__</code> method, which I tried to use to extract the name of the operator like so:</p>

<pre><code>def __getattr__(self, name):
    print('Method name:', name.strip('_'))
</code></pre>

<p>But, unfortunately, this only works when called using the full method name, not when an operator is used. How can I write a one-size-fits-all binary operator definition?</p>

<p>You could use a class decorator to mutate your class and add them all in with the help of a factory function:</p>

<pre><code>import operator

def natural_binary_operators(cls):
    for name, op in {
        '__add__': operator.add,
        '__sub__': operator.sub,
        '__mul__': operator.mul,
        '__truediv__': operator.truediv,
        '__floordiv__': operator.floordiv,
        '__and__': operator.and_,
        '__or__': operator.or_,
        '__xor__': operator.xor
    }.items():
        setattr(cls, name, cls._make_binop(op))

    return cls


@natural_binary_operators
class Vector(tuple):
    @classmethod
    def _make_binop(cls, operator):
        def binop(self, other):
            try:
                return cls([operator(a, x) for a, x in zip(self, other)])
            except:
                return cls([operator(a, other) for a in self])

        return binop
</code></pre>

<p>There are a few other ways to do this, but the general idea is still the same.</p>

python class