举例说明Python 中__mod__() 的方法

563 阅读2分钟

语法

object.__mod__(self, other)

Python__mod__() 方法实现了模数运算 % ,默认情况下返回左操作数除以右操作数的剩余部分。在内部,Python 试图调用x.__mod__(y) 来实现模运算x%y 。如果这个方法没有实现,Python 首先尝试在右边的操作数上调用__rmod__ ,如果这个方法也没有实现,它就会引发一个TypeError

我们把这种方法称为 "Dunder Method",即*"Double UnderscoreMethod"(也称为"Magic Method")*。

例子

在下面的例子中,你创建了一个自定义类Data ,并重写了__mod__() 方法,以便在尝试计算两个数字的模数时返回一个假字符串。

class Data:
        
    def __mod__(self, other):
        return '... my result of mod...'


a = Data()
b = Data()
c = a % b

print(c)
# ... my result of mod...

如果你没有定义__mod__() 方法,Python 会产生一个TypeError

如何解决TypeError:%的操作数类型不支持

考虑下面的代码片段,你试图在没有定义dunder方法的情况下计算两个自定义对象的modulo__mod__()

class Data:
    pass


a = Data()
b = Data()
c = a % b

print(c)

在我的电脑上运行这段代码会出现以下错误信息。

Traceback (most recent call last):
  File "C:\Users\xcent\Desktop\code.py", line 7, in <module>
    c = a % b
TypeError: unsupported operand type(s) for %: 'Data' and 'Data'

这个错误的原因是,__mod__() dunder方法从来没有被定义过--而且默认情况下,它没有为自定义对象定义。所以,为了解决TypeError: unsupported operand type(s) for % ,你需要在你的类定义中提供__mod__(self, other) 方法,如下所示:

class Data:
        
    def __mod__(self, other):
        return '... my result of mod...'

Python __mod__ vs __rmod__

假设,你想计算两个自定义对象xy 的模数。

print(x % y)

Python 首先尝试调用左边对象的__mod__() 方法x.__mod__(y) 。但这可能因为两个原因而失败。

  1. 方法x.__mod__() 首先没有实现,或者
  2. 方法x.__mod__() 已经实现,但是返回一个NotImplemented 值,表明数据类型不兼容。

如果失败了,Python 试图通过调用y.__rmod__() 在右操作数上的反向模数 y 来解决这个问题。

如果这个方法被实现,Python 知道它没有遇到非交换性操作的潜在问题。如果它只是执行y.__mod__(x) 而不是x.__mod__(y) ,结果将是错误的,因为模运算是非交换性的。这就是为什么需要y.__rmod__(x)

因此,x.__mod__(y)x.__rmod__(y) 之间的区别是,前者计算x % y 而后者计算y % x - 两者都调用定义在对象x 上的各自的模数方法。

你可以在这里看到这个效果,我们试图在左边的操作数x 上调用模数运算 - 但是由于它没有实现,Python 只是在右边的操作数y 上调用反向的模数运算。

class Data_1:
    pass

class Data_2:
    def __rmod__(self, other):
        return 'called rmod'


x = Data_1()
y = Data_2()

print(x % y)
# called rmod