在 Python 中使用重载加法运算符时出现内存错误

Posted

技术标签:

【中文标题】在 Python 中使用重载加法运算符时出现内存错误【英文标题】:Memory error when using overloaded addition operator in Python 【发布时间】:2021-05-21 11:38:55 【问题描述】:

我在P1 类中重载了加法运算符,使实例可以直接相加。但是,在执行p1 + p2 时会发生内存错误。 p1.data + p2.data 可以给出正确的结果。

class P1:
    def __init__(self):
        self.data = np.zeros((512,512),dtype='float64')
    def __setitem__(self, key, value):
        self.data[key] = value
    def __getitem__(self, item):
        return self.data[item]
    
    def __add__(self, other):
        return self.data + other
    
    def __radd__(self, other):
        return other + self.data

p1 = P1()
p2 = P1()
p1 + p2

Traceback (most recent call last):
  File "D:\professional\Anaconda_install\lib\site-packages\IPython\core\interactiveshell.py", line 3418, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-224-a0c3d63f397c>", line 1, in <module>
    p1 + p2
  File "<ipython-input-221-ba43c19b29b0>", line 12, in __add__
    return self.data + other
  File "<ipython-input-221-ba43c19b29b0>", line 15, in __radd__
    return other + self.data
MemoryError: Unable to allocate 2.00 MiB for an array with shape (512, 512) and data type float64

谁能帮我解释一下?

【问题讨论】:

【参考方案1】:

发生过载是因为您在 __add____radd__ 中说的是 other 而不是 other.data,这会创建一个 Feedback Loop。

具体来说,您首先调用p1.__add__(p2),这会导致解释器查找p2.__radd__(p1),这会重复该过程,因为p2.__radd__(p1) 会查找p1.__add__(p2)

也就是说,它永远不会返回,因为它会查找内容,在找到更多数据后保存值以完成计算,并在这个无休止的过程中耗尽可用内存。

解决方案:

import numpy as np

class P1:
    def __init__(self):
        self.data = np.zeros((512,512),dtype='float64')
    def __setitem__(self, key, value):
        self.data[key] = value
    def __getitem__(self, item):
        return self.data[item]

    def __add__(self, other):
        # return self.data + other.data
        return np.array([*self.data, *other.data])
    def __radd__(self, other):
        # return other.data + self.data
        return np.array([*other.data, *self.data])

虽然从技术上讲,您可以只交换__add____radd__ 之一,并且运算符重载仍然有效。

【讨论】:

感谢您的解释。为什么当 self.data 是一个值时它会起作用,而当 self.data 是一个数组时它会失败?您能否详细解释一下当口译员致电p2.__radd__(p1)p1.__add__(p2) 时会发生什么? 因为 python 列表没有__radd__ 属性。我个人无法详细介绍解释器,但 __add____radd__ 只是“魔术”或“dunder”(双下划线)方法,它们为拥有它们的类提供了 @987654338 的“语法糖” @ 和 b + a,分别。每当您在对象上使用运算符符号时,解释器都会查找其魔术方法,以便它知道要做什么。如果找不到,则会引发错误。从技术上讲,您可以将任何旧东西放在那里 编辑了帖子,应该会更好。我没有意识到列表没有__radd__

以上是关于在 Python 中使用重载加法运算符时出现内存错误的主要内容,如果未能解决你的问题,请参考以下文章

在 SYCL 中实现矩阵加法和乘法

在 SYCL 中实现矩阵加法和乘法

复数类重载加法减法和乘法运算符

重载抽象运算符 = 时出现 Clang 链接器错误

Python 触“类”旁通4|重载运算符之单链表的“加减乘除”

c++ 运算符重载问题