【问题标题】:TypeError after overriding the __add__ method覆盖 __add__ 方法后的 TypeError
【发布时间】:2011-02-22 18:25:01
【问题描述】:

我想了解__add__ 的工作原理:

class MyNum:
    def __init__(self,num):
        self.num=num
    def __add__(self,other):
        return MyNum(self.num+other.num)
    def __str__(self):
        return str(self.num)

如果我把它们放在一个列表中

d=[MyNum(i) for i in range(10)]

这行得通

t=MyNum(0)
for n in d:
    t=t+n
print t

但这不是:

print sum(d)
TypeError: unsupported operand type(s) for +: 'int' and 'instance'

我做错了什么?如何让 sum() 工作?

我的问题是如何在支持__add__ 的对象列表上使用总和,需要使其尽可能通用。

【问题讨论】:

    标签: python


    【解决方案1】:

    您还需要定义 __radd__ 才能使其正常工作。

    __radd__反向添加。当 Python 尝试评估 x + y 时,它首先尝试调用 x.__add__(y)。如果失败,则返回到y.__radd__(x)

    这允许您通过仅触摸一个类来覆盖添加。例如,考虑 Python 如何评估 0 + x。尝试调用0.__add__(x),但int 对您的课程一无所知。您不能很好地更改int 中的__add__ 方法,因此需要__radd__。我想这是一种依赖倒置的形式。

    正如 Steven 所指出的,sum 在原地运行,但从 0 开始。所以第一个添加是唯一需要使用 __radd__ 的添加。作为一个很好的练习,您可以检查是否是这种情况!

    【讨论】:

    • 对我来说很合适。另见stackoverflow.com/questions/1218710/…
    • @hop:大卫赫弗南是正确的。实施__radd__ 有效。我仔细检查了__radd__ 的测试实现,它工作得很好。
    • @David Heffernan:我知道__radd__ 做了什么,但也许 Ηλίας 不知道。在你的答案中包含解释,我会添加我的 +1。
    • @Ηλίας:阅读此答案:stackoverflow.com/questions/1218710/…
    • @Fred @Steven @Ηλίας @hop 对不起,我希望现在更好。我匆忙下班,所以无法详细说明。我认为谷歌会做剩下的事情。无论如何,我认为现在更好。
    【解决方案2】:
    >>> help(sum)
    Help on built-in function sum in module __builtin__:
    
    sum(...)
        sum(sequence[, start]) -> value
    
        Returns the sum of a sequence of numbers (NOT strings) plus the value
        of parameter 'start' (which defaults to 0).  When the sequence is
        empty, returns start.
    

    换句话说,提供一个起始值:

    sum(d, MyNum(0))
    

    从我的以下评论中粘贴的编辑:

    sum 使用整数零的默认起始值​​。您所写的MyNum 类不知道如何将自身添加到整数中。要解决这个问题,您有两个选择。您可以为sum 提供一个与您的类具有相同类型的起始值,或者您可以实现__radd__Python 在添加不同类型的值时调用它(例如当第一个d 中的值添加到默认起始值​​零)。

    【讨论】:

    • 谁投了-1票,想知道为什么?回答引用文档并且在技术上是正确的。
    • @hop:我对此进行了测试,它确实有效。你能扩展你的评论吗?
    • 所以 sum 只有在你继承 int 时才有效? d=[MyNum(i) for i in range(10)] 然后 sum(0,d) 失败
    • @Ηλίας:sum 使用整数零的默认起始值​​。您所写的MyNum 类不知道如何将自身添加到整数中。要解决这个问题,您有两个选择。您可以为sum 提供与您的类相同类型的起始值,或者您可以实现__radd__,当添加不同类型的值时(例如添加d 中的第一个值时)到零的起始值)。
    【解决方案3】:
    class MyNum:
        def __init__(self,num):
            self.num=num
        def __add__(self,other):
            return self.num += other.num
        def __str__(self):
            return str(self.num)
    
    one = MyNum(1)
    two = MyNum(2)
    
    one + two
    
    print(two.num)
    

    【讨论】:

    • OP 试图理解代码,但你只是简单地转储了代码,没有任何解释。请edit您的答案包括对您的代码的描述并实际回答问题。如果你不想这样,你可以删除你的答案,因为它完全没有用。
    【解决方案4】:

    另一个选项是 reduce(Python 3.x 中的 functools.reduce)。

    from functools import reduce
    from operators import add
    d=[MyNum(i) for i in range(10)]
    my_sum = reduce(add,d)
    

    【讨论】:

      【解决方案5】:

      我反对用起点转发sum(),下面暴露的漏洞,

      In [51]: x = sum(d, MyNum(2))
      
      In [52]: x.num
      Out[52]: 47
      

      想知道为什么你在期待的时候得到了 47 …从 MyNum() 的第二个开始,先离开,然后添加它们直到结束,所以预期结果 = 44 (sum(range(2,10))

      这里的事实是 2 不是作为起始对象/位置,而是作为结果的补充

      总和(范围(10))+ 2

      哎呀,链接坏了!!!!!!

      使用radd

      下面是正确的代码。另请注意以下内容

      仅当 + 右侧的对象是您的类实例时,Python 才会调用 __radd__ 例如:2 + obj1

      #!/usr/bin/env python
      
      class MyNum:
          def __init__(self,num):
              self.num=num
      
          def __add__(self,other):
              return MyNum(self.num+other.num)
      
          def __radd__(self,other):
              return MyNum(self.num+other)
      
          def __str__(self):
              return str(self.num)
      
      d=[MyNum(i) for i in range(10)]
      print sum(d)    ## Prints 45
      d=[MyNum(i) for i in range(2, 10)]
      print sum(d)    ## Prints 44
      print sum(d,MyNum(2))   ## Prints 46 - adding 2 to the last value (44+2)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-04
        • 1970-01-01
        相关资源
        最近更新 更多