python - 一次重载多个运算符

时间:2013-03-04 07:12:46

标签: python operator-overloading

我有一个自定义类,我想重载几个artihmetic操作符,并想知道是否有办法避免必须单独写出每个代码的代码。我无法找到任何不明确重载每个运算符的示例。

class Foo(object):
    a=0 

    def __init__(self, a):
        self.a=a            

    def __add__(self, other):
        #common logic here
        return Foo(self.a+other.a)

    def __sub__(self, other):
        #common logic here  
        return Foo(self.a-other.a)

    def __mul__(self, other):
        #common logic here
        return Foo(self.a*other.a)

#etc...

逻辑稍微复杂一点,但常见的模式是每个运算符重载方法包含一些相同的代码来检查操作是否被允许,然后使用类成员构造一个操作。我想减少冗余代码。这有效:

class Foo(object):
    a=0 

    def __init__(self, a):
        self.a=a            

    def operate(self, other, operator):
        #common logic here
        a = constructOperation(self.a, other.a, operator)
        return Foo(a)

    def __add__(self, other):
        return self.operate(other, "+")

    def __sub__(self, other):       
        return self.operate(other, "-")     


def constructOperation(operand0, operand1, operator):
    if operator=="+": 
        return operand0 + operand1
    if operator=="-": 
        return operand0 - operand1

但是像这样手动构建操作似乎有点愚蠢。这种方法是否有意义,或者这里有更好的方法吗?

4 个答案:

答案 0 :(得分:5)

你可以通过反射和高阶函数来完成它,尽管这可能不适合继承。

import operator

def apply_a(func):
    def inner(self, other):
        return Foo(func(self.a, other.a))
    return inner

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

for name in ['__add__','__mul__','__sub__']:
    setattr(Foo, name, apply_a(getattr(operator, name)))

答案 1 :(得分:5)

我只想使用operator模块:

import operator

class Foo(object):

    a=0 

    def __init__(self, a):
        self.a=a            

    def operate(self, other, op):
        #common logic here
        return Foo(op(self.a, other.a))

    def __add__(self, other):
        return self.operate(other, operator.add)

    def __sub__(self, other):       
        return self.operate(other, operator.sub)     

答案 2 :(得分:1)

我不知道是否有办法避免定义所有(或至少大多数)运算符。没有一个是有意义的。毕竟,在给定__sub__(和__add__)的情况下,没有一种方法可以定义__mul__。 但是,一个改进是将callables传递给constructOperation()而不是符号运算符。

E.g。

class Foo(object):
    a=0 

    def __init__(self, a):
        self.a=a            

    def operate(self, other, operator):
        #common logic here
        a = constructOperation(self.a, other.a, operator)
        return Foo(a)

    def __add__(self, other):
        return self.operate(other, sum)

    def __sub__(self, other):       
        return self.operate(other, lambda x, y: x - y)     


def constructOperation(operand0, operand1, operator):
    return operator(operand0, operand1)

答案 3 :(得分:1)

必须定义方法。这是因为python在调用特殊方法时会进行特殊的查找,因此会这样:

import operator

class Foo(object):

    def __init__(self, a):
        self.a = a
    def __getattr__(self, attr):
        try:
            func = getattr(operator, attr)
            return lambda x: Foo(func(self.a, x.a))
        except AttributeError:
            raise AttributeError(attr)

不起作用:

>>> f = Foo(1)
>>> g = Foo(3)
>>> f * g     #no __mul__?
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for *: 'Foo' and 'Foo'
>>> getattr(f, '__mul__')
<function <lambda> at 0x2975140>
>>> f.__mul__(g)
<__main__.Foo object at 0x2922ed0>
>>> f.__mul__(g).a
3

你能做的“最好”就是使用锑的解决方案,这是最干的。