代码之家  ›  专栏  ›  技术社区  ›  Peter Fletcher

是否可以重载Python二进制“-”运算符,使其在类之前使用一个普通数字?[副本]

  •  -1
  • Peter Fletcher  · 技术社区  · 2 年前

    当我遇到 __rsub__ 方法虽然我能找到 explanation on the method in the official documentation :

    调用这些方法来实现二进制算术运算( + , - , * , / , // , % , divmod() , pow() , ** , << , >> , & , ^ , | )具有反射(交换)操作数。只有当左操作数不支持相应的操作并且操作数的类型不同时,才会调用这些函数。例如,要计算表达式 x - y 哪里 y 是一个类的实例,该类具有 __rsub__() 方法 y.__rsub__(x) 如果 x.__sub__(y) 退货 NotImplemented

    我无法想象为什么这种方法是必要的,以及它在现实中是如何使用的。

    你能给我一个该方法有用的典型环境吗?

    0 回复  |  直到 6 年前
        1
  •  11
  •   ShadowRanger    9 年前

    基本示例。你自己写 int -同类:

    class FooInt:
        ... other stuff elided ...
    
        def __sub__(self, other):
            if isinstance(other, FooInt):
                return self.__class__(self.intvalue - other.intvalue)
            elif isinstance(other, int):
                return self.__class__(self.intvalue - other)
            else:
                return NotImplemented
    

    现在您有了这样的代码:

    FooInt(123) - 456
    

    这很好;Python看到 FooInt 在左边,看到它有 __sub__ ,和呼叫 FooInt.__sub__(FooInt(123), 456) 它返回时没有错误,我们很好。

    接下来我们看到:

    123 - FooInt(456)
    

    Python尝试调用 int.__sub__(123, FooInt(456)) 但是 整数 不知道如何处理 FooInt公司 ,和返回 NotImplemented ;它不知道 intvalue 具有可用于此目的的价值。在这一点上,Python不能仅仅调用 FooInt.__sub__(FooInt(456), 123) 因为它不能假设减法是可交换的(事实上,就像在大多数数值系统中一样,在这种情况下,减法是不可交换的,你不能只交换算子的左右两边就得到正确的结果)。这就是为什么 __rsub__ 存在;它允许您检查另一个类是否有处理操作的方法,同时告诉它两件事:

    1. 它位于二进制运算符的右侧(允许它正确处理交换性)
    2. 左边的项目不知道如何处理操作,所以这是最后一次纠正错误的机会

    第二点也非常重要。实施时 __xxx__ (左手操作符)你想在你识别的类型上非常保守。如果你不知道你正在使用一个已知的正确处理程序处理一个已知具体类型,那么你不应该试图处理未知类型;另一个类型可能知道如何正确执行操作,因此您返回 未实现 让另一方来处理 __rxxx__ ,你是最后的机会;另一个人不知道该怎么办,所以你应该对你接受的事情持自由态度,如果你有任何方法来处理它,就尽你所能。你可以在上的Python文档中看到这一点 Implementing the arithmetic operations ;这个 __xxx__ 混凝土类型的操作检查( Fraction ,它检查 小部分 , 整数 , float complex ),而 __rxxx__ 操作员检查通用接口( numbers.Integral , numbers.Rational , numbers.Real 等等)。

    我忽略了一个相对重要的观点: If the class on the right side is a subclass of the class on the left, it has its reflected __rxxx__ method called first ;假设子类将有更多的信息来正确地确定要做什么,所以它首先尝试执行操作。