【问题标题】:Auto-generating the overloaded operators?自动生成重载运算符?
【发布时间】:2019-12-09 14:55:21
【问题描述】:

考虑以下在 Python 中具有所有向量操作的向量对象的实现:

import operator

class Vector:

    def __init__(self, value):
        self._vals = value.copy()


    @classmethod
    def _op(cls, this, that, oper, rev=False):

        assert isinstance(this, cls)

        if rev:
            op = lambda a, b : oper(b, a)
        else:
            op = oper

        if isinstance(that, list):
            result = [op(x, y) for (x, y) in zip(this._vals, that)]
        elif isinstance(that, cls):
            result = [op(x, y) for (x, y) in zip(this._vals, that._vals)]
        else:
            # assume other is scalar
            result = [op(x, that) for x in this._vals]

        return cls(result)

    def __add__(self, other):
        return Vector._op(self, other, operator.add, False)


    def __radd__(self, other):
        return Vector._op(self, other, operator.add, True)

    def __sub__(self, other):
        return Vector._op(self, other, operator.sub, False)

    def __rsub__(self, other):
        return Vector._op(self, other, operator.sub, True)

    def __mul__(self, other):
        return Vector._op(self, other, operator.mul, False)

    def __rmul__(self, other):
        return Vector._op(self, other, operator.mul, True)

    def __truediv__(self, other):
        return Vector._op(self, other, operator.truediv, False)

    def __rtruediv__(self, other):
        return Vector._op(self, other, operator.truediv, True)

    def __str__(self):
        return str(self._vals)

很明显,所有重载的运算符(__add____radd__、...)都有完全相同的代码,除了传递给私有类方法 @ 的 operrev 参数987654327@。

有没有办法避免所有的复制粘贴和自动(或半自动)这些操作?

【问题讨论】:

标签: python operator-overloading


【解决方案1】:

第一件事:类方法也可以在实例上使用,所以你应该把你的方法写成

def __someop__(self, other):
    return self._op(self, other, ...)

另外,获取对象的类型 (cls = type(obj)) 也很容易,因此除非您有其他理由将 _op 设为类方法,否则您可以将其设为实例方法。

现在回答您的问题:从技术上讲,您可以使用自定义元类或类装饰器来创建方法,即:

class VectorType(type):
    def __new__(meta, name, bases, attrs):
        todo = [
            # name, operator, reverse
            ("__add__", operator.add, False), 
            ("__sub__", operator.sub, False),
            # etc
            ]
         for name, oper, rev in todo:
             # avoids overriding already defined methods
             if name not in attrs:
                 def f(self, other):  
                     return cls._op(self, other, oper=oper, rev=rev)
                 attrs[name] = f

          return type.__new__(meta, name,   bases, attr) 

class Vector(metaclass=VectorType):
    # etc

但这确实不会改进你现有的代码,除非你有很多(不相关的)类使用这个方案,即使这样,一个普通的 mixin 类也可以工作。

如果您只有这个类的问题,那么您当前的代码是清晰、简单、易读和明确的,IOW 完美的 Pythonic。

【讨论】:

  • 特殊方法不是总是使用对象的类来查找的吗?这意味着它们不能像您声称的那样仅作为实例方法。
  • @martineau 恐怕我没有得到你的评论......特殊方法只在类上查找是的,但这并不意味着它们必须是类方法(实际上它们不应该) - 我建议转换为实例方法的唯一方法无论如何都不是魔术方法。
  • @martineau 看起来你正在混淆“实例方法”(iow 普通方法,在类上定义并将实例作为第一个参数)与作为实例添加的“每个实例方法”iow 方法属性(这显然不是我的答案的一部分)
猜你喜欢
  • 2011-05-15
  • 1970-01-01
  • 2012-03-11
  • 1970-01-01
  • 2012-05-16
  • 1970-01-01
  • 2020-02-04
  • 1970-01-01
相关资源
最近更新 更多