【问题标题】:Can you impose object precedence for overloaded operators in Python?你可以为 Python 中的重载运算符强加对象优先级吗?
【发布时间】:2014-03-09 22:31:50
【问题描述】:

假设我有两个 Python 类,它们都定义了 add 和 radd 运算符重载,并且我将一个类的一个实例添加到另一个类的另一个实例。选择的实现取决于添加项目的顺序(Python 首先在 LHS 上查找 add 方法,等等)。

我是否可以定义首选对象实现的优先级?例如,如果 radd 的优先级高于 LHS,我希望在 RHS 上调用 radd。

我真的很想对所有重载的运算符都这样做,所以我最终想要一个更通用的解决方案。

[编辑:添加示例]

例如,我可能有一个自定义数字类型类,我可能希望将 int 静默类型转换为我的自定义类型。因此我需要 add 和 radd (以及所有其他运算符重载与他们的 'r' 表亲)。

接下来,我想要一个通用多项式类,其系数是一些通用数字类型。我还想将事物类型转换为多项式,因此我为它实现了 add 和 radd 函数。但是,如果我在左侧添加一个自定义数字,在右侧添加一个多项式,我希望将其 up 类型转换为多项式,而不是 Python 尝试对其进行类型转换 down 自定义号码类型。因此,我希望调用 radd 而不是 add。

【问题讨论】:

  • 举个例子就好了,因为到目前为止我似乎没能帮到你。
  • 到目前为止,您没有帮助我,因为您误读了我的问题,并且经过两次修订后,BrenBarn 在他的简短回答中所说的要少得多。
  • 当然,只是为了所有阅读本文的人的利益而解释一下 Python 的对象方法发生了什么。我真的很想看看你对这个优先想法的用例。
  • @AaronHall 刚刚添加了示例。
  • 我认为您的编辑偏离了真正的问题。内置类型已经遵循自定义 __radd__ 方法,因为如果你这样做 3 + MyCustomInt(),你的自定义类的 __radd__ 确实会被调用。如果我理解正确,您问题的本质是您希望能够对自定义类做同样的事情。

标签: python operator-overloading operator-precedence


【解决方案1】:

正如您所描述的,没有用于定义“优先级”的内置机制。您可以做的是在魔术方法中实现优先级检查。也就是说,您可以定义对象的__add__ 方法,以便它检查另一个对象的“优先级”(无论如何定义),如果其优先级更高,则调用该对象的__add__(或__radd__)。

请注意,如果您只是希望 LHS __add__ 遵循 RHS __radd__,您可以返回 NotImplemented,这实际上会告诉 Python“就像您刚刚调用的 __add__ 方法没有存在”。

以下是如何在魔术方法上使用装饰器完成此操作的草图:

def deco(op):
    def newOp(self, other):
        if other.precedence > self.precedence:
            return NotImplemented
        return op(self, other)
    return newOp

class Thing(object):
    precedence = 0

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

    @deco
    def __add__(self, other):
        print "Called", self, "__add__"
        return self.__class__(self.val + other.val)

    def __radd__(self, other):
        print "Called", self, "__radd__"
        return self.__class__(self.val + other.val)

class Weak(Thing):
    precedence = 1

class Strong(Thing):
    precedence = 2

这导致无论操作数的顺序如何,总是调用Strong 版本,因此它总是返回Strong

>>> Weak(1) + Strong(1)
Called <__main__.Strong object at 0x01F96BF0> __radd__
<__main__.Strong object at 0x01F96BD0>
>>> Strong(1) + Weak(1)
Called <__main__.Strong object at 0x01F96B90> __add__
<__main__.Strong object at 0x01F96250>

【讨论】:

  • 我试图想出一种优雅的方式来做到这一点,但我能想到的最好的方法是有一个装饰器来检查函数的名称,在第二个下划线后拼接一个“r”(Python 3 兼容性,哎呀!)如果优先级更高,则调用它。它看起来并不那么优雅……还有更好的主意吗?
  • @JeremyKun:查看我编辑的答案。您可以返回NotImplemented 转嫁责任。但是,您的评论表明您正在尝试做一些比您的问题更笼统的事情。如果你只是想打电话给__radd__,你不需要检查名称和接头或任何花哨的东西;你只需重写__add__ 方法,让它在适当的情况下直接调用other.__radd__
  • 提高(返回?)NotImplemented 是一个很好的提示!我想要一种更通用的方法的原因是因为我想对 sub、mul、div 和许多其他重载做同样的事情。
  • 我的错,刚刚知道 NotImplemented 实际上是一个内置常量。
  • @JeremyKun:将其添加到您的问题中可能会很好。在许多情况下,可能有一种简单但相对不灵活的方法可以为一种方法执行此操作,如果您需要为许多方法执行此操作,则可能有一种更复杂但更全面的方法。从您的评论中,我收集到您也许可以使用您的装饰器想法弄清楚如何使用NotImplemented 来做到这一点。
【解决方案2】:

如果使用+ 运算符未在第一个项目上实现__add__,Python 将使用__radd__

foo + bar

将尝试在otherbar 上使用foo__add__ 运算符。如果没有实现,它将调用bar__radd__

class Foo(object):
    pass

class Bar(object):
    def __radd__(self, other):
        print('Bar.__radd__ was called!')

>>> foo = Foo()
>>> bar = Bar()
>>> foo + bar
Bar.__radd__ was called!

当 Foo 有 __add__ 时,它会获得优先权:

class Foo(object):
    def __add__(self, other):
        print('Foo.__add__ was called!')

>>> foo = Foo()
>>> foo + bar
Foo.__add__ was called!

如果两者都实现了,则不能强制 Python 执行任何特殊操作,优先顺序是预定义的。但是,您可以检查其他人的存在:

class Foo(object):
    def __add__(self, other):
        if hasattr(other, '__radd__'):
            return other.__radd__(self)
        else:
            print('Foo.__add__ was called!')

>>> foo = Foo()
>>> foo + bar
Bar.__radd__ was called!

【讨论】:

  • 这并不能回答我的问题,即如果在两个对象上都定义了 add 和 radd ,则强制 Python 做一些特殊的事情。
  • @JeremyKun 更新了我的答案,你怎么看?
  • 最后一句是唯一相关的(从我的问题中可以清楚地看出,我了解现有的优先规则),并且 BrenBarn 已经给出了答案。
  • @JeremyKun 我现在演示如何做到这一点。
猜你喜欢
  • 1970-01-01
  • 2012-10-08
  • 2017-07-27
  • 2021-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-10
  • 1970-01-01
相关资源
最近更新 更多