【问题标题】:Memory error when using overloaded addition operator in Python在 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

谁能帮我解释一下?

【问题讨论】:

    标签: python python-3.x class operator-overloading python-class


    【解决方案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__
    猜你喜欢
    • 2020-04-15
    • 1970-01-01
    • 1970-01-01
    • 2015-05-01
    • 2012-08-21
    • 2015-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多