类定义中的泛型二进制操作?

时间:2016-09-22 17:01:29

标签: python class

我正在Python 3中编写一个小的线性代数模块,并且有许多二元运算符要定义。由于二元运算符的每个定义基本上是相同的,只有运算符本身发生了变化,我想通过只编写一次通用的二元运算符定义来保存一些工作。

例如:

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 & x for a, x in zip(self, xs))
        except:
            return Vector(a & x for a in self)

    ... # mul, div, or, sub, and all other binary operations

上面的二元运算符具有相同的形式。仅更改操作员。我想知道我是否可以立即编写所有操作符,如下所示:

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)

我听说Python可以使用__getattr__方法做一些神奇的事情,我试图用它来提取运算符的名称,如下所示:

def __getattr__(self, name):
    print('Method name:', name.strip('_'))

但是,不幸的是,这只有在使用完整方法名称调用时才有效,而不是在使用运算符时。如何编写一个适合所有人的二元运算符定义?

4 个答案:

答案 0 :(得分:2)

您可以使用类装饰器来改变您的类,并在工厂函数的帮助下将它们全部添加:

lib32ncurses5

还有其他一些方法可以做到这一点,但总体思路仍然相同。

答案 1 :(得分:1)

您可以使用operator模块执行此操作,该模块为您提供操作符的功能版本。例如,operator.and_(a, b)a & b相同。

因此return Vector(a + x for a in self)变为return Vector(op(a, x) for a in self),您可以参数化op。你仍然需要定义所有魔术方法,但它们可以是简单的传递方式。

答案 2 :(得分:1)

更新

这可能超级慢,但您可以使用所有二进制方法创建一个抽象类并从中继承。

import operator

def binary_methods(cls):
    operator_list = (
        '__add__', '__sub__', '__mul__', '__truediv__', 
        '__floordiv__', '__and__', '__or__', '__xor__'
    )

    for name in operator_list:
        bop = getattr(operator, name)
        method = cls.__create_binary_method__(bop)
        setattr(cls, name, method)

    return cls

@binary_methods
class AbstractBinary:
    @classmethod
    def __create_binary_method__(cls, bop):
        def binary_method(self, xs):
            try:
                return self.__class__(bop(a, x) for a, x in zip(self, xs))
            except:
                return self.__class__(bop(a, x) for a in self)
        return binary_method

class Vector(AbstractBinary, tuple):
    def __new__(self, x):
        return super(self, Vector).__new__(Vector, x)

原件:

好的,我认为我有一个工作解决方案(仅在Python 2.X中测试),它使用类装饰器动态创建二进制方法。

import operator

def add_methods(cls):
    operator_list = ('__add__', '__and__', '__mul__')

    for name in operator_list:
        func = getattr(operator, name)
        # func needs to be a default argument to avoid the late-binding closure issue
        method = lambda self, xs, func=func: cls.__bop__(self, func, xs)
        setattr(cls, name, method)

    return cls

@add_methods
class Vector(tuple):
    def __new__(self, x):
        return super(self, Vector).__new__(Vector, x)

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

以下是一些示例用法:

v1 = Vector((1,2,3))
v2 = Vector((3,4,5))

print v1 * v2 
# (3, 8, 15)

答案 3 :(得分:0)

可以__getattr__做一些神奇的事情,但如果你能避免这样做,那么我会 - 它开始变得复杂!在这种情况下,你可能需要覆盖__getattribute__,但请不要因为如果你开始搞乱__getattribute__,你会咬自己的座位。< / p>

您可以通过简单地定义第一个,然后在其他函数中执行__and__ = __add__,以非常简单的方式实现此目的。

class MyClass(object):
    def comparison_1(self, thing):
        return self is not thing

    comparison_2 = comparison_1


A = MyClass()

print A.comparison_1(None)
print A.comparison_2(None)
print A.comparison_1(A)
print A.comparison_2(A)

给出

$ python tmp_x.py 
True
True
False
False

但是,我不是这种hackery的粉丝。我会做的

class MyClass(object):
    def comparison_1(self, thing):
        "Compares this thing and another thing"
        return self is not thing

    def comparison_2(self, thing):
        "compares this thing and another thing as well"
        return self.comparison_1(thing)

为了清晰起见,最好写出额外的几行。

编辑:

所以我用__getattribute__尝试了它,不起作用:/。我承认我不知道为什么。

class MyClass(object):
    def add(self, other):
        print self, other
        return None

    def __getattribute__(self, attr):
        if attr == '__add__':
            attr = 'add'

        return object.__getattribute__(self, attr)


X = MyClass()
print X.__add__
X + X

不能工作:/

andy@batman[18:15:12]:~$ p tmp_x.py 
<bound method MyClass.add of <__main__.MyClass object at 0x7f52932ea450>>
Traceback (most recent call last):
  File "tmp_x.py", line 15, in <module>
    X + X
TypeError: unsupported operand type(s) for +: 'MyClass' and 'MyClass'