如何委托超类的__add__方法?

时间:2012-11-12 12:39:09

标签: python oop inheritance override delegation

说我有这个班:

class MyString(str):
    def someExtraMethod(self):
        pass

我希望能够做到

a = MyString("Hello ")
b = MyString("World")
(a + b).someExtraMethod()
("a" + b).someExtraMethod()
(a + "b").someExtraMethod()
  1. 按原样运行:

    AttributeError: 'str' object has no attribute 'someExtraMethod'
    
  2. 显然这不起作用。所以我补充一点:

    def __add__(self, other):
        return MyString(super(MyString, self) + other)
    def __radd__(self, other):
        return MyString(other + super(MyString, self))
    
    TypeError: cannot concatenate 'str' and 'super' objects
    
  3. 嗯,好的。 super似乎不尊重运算符重载。也许:

    def __add__(self, other):
        return MyString(super(MyString, self).__add__(other))
    def __radd__(self, other):
        return MyString(super(MyString, self).__radd__(other))
    
    AttributeError: 'super' object has no attribute '__radd__'
    
  4. 仍然没有运气。我该怎么办?

2 个答案:

答案 0 :(得分:9)

我通常使用super(MyClass, self).__method__(other)语法,在您的情况下这不起作用,因为str不提供__radd__。但您可以使用str将类的实例转换为字符串。

谁说它的版本3有效:它没有:

>>> class MyString(str):
...     def __add__(self, other):
...             print 'called'
...             return MyString(super(MyString, self).__add__(other))
... 
>>> 'test' + MyString('test')
'testtest'
>>> ('test' + MyString('test')).__class__
<type 'str'>

如果您实施__radd__,则会获得AttributeError(请参阅下面的说明)。

无论如何,我会避免使用内置函数作为基类型。正如您所看到的,某些细节可能很棘手,您还必须重新定义所有它们支持的操作,否则该对象将成为内置实例,而不是您类的实例。 我认为在大多数情况下,使用委托而不是继承更容易。

此外,如果您只想添加一个方法,那么您可以尝试在普通字符串上使用函数。


我在这里添加了一些关于为什么str没有提供__radd__的解释以及当python执行BINARY_ADD操作码(执行+的操作码时发生的事情) )。

str.__radd__的绝对是由于字符串对象实现了序列的连接运算符而不是数字加法运算。其他序列也是如此,例如listtuple。这些在“Python级别”是相同的,但实际上在C结构中有两个不同的“槽”。

数字+运算符,在python中由两个不同的方法(__add____radd__定义)实际上是一个单独的C函数,使用交换参数调用来模拟调用到__radd__

现在,您可以认为只实施MyString.__add__会解决您的问题,因为str没有实现__radd__,但事实并非如此:

>>> class MyString(str):
...     def __add__(self, s):
...             print '__add__'
...             return MyString(str(self) + s)
... 
>>> 'test' + MyString('test')
'testtest'

正如您所看到的,MyString.__add__未被调用,但如果我们交换参数:

>>> MyString('test') + 'test'
__add__
'testtest'

它被称为,所以发生了什么?

答案在documentation中,其中指出:

  

对于对象xy,首先尝试x.__op__(y)。如果不是这样的话   实施或返回NotImplemented,尝试y.__rop__(x)。如果这   也未实现或返回NotImplementedTypeError   提出异常。但请参阅以下例外:

     

前一项的异常:如果左操作数是。的实例   内置类型或新样式类,右操作数是一个   该类型或类的正确子类的实例并覆盖   base的__rop__()方法,尝试使用右操作数的__rop__()方法   在左操作数的__op__()方法之前。

     

这样做是为了使子类可以完全覆盖二进制   运营商。否则,左操作数的__op__()方法将始终如此   接受正确的操作数:当给定类的实例是   期望,该类的子类的实例始终是   可以接受的。

这意味着您必须实现所有 str __r*__方法的方法,否则您仍会遇到参数顺序问题

答案 1 :(得分:2)

可能是:

class MyString(str):
  def m(self):
    print(self)

  def __add__(self, other):
    return MyString(str(self) + other)

  def __radd__(self, other):
    return MyString(other + str(self))    

a = MyString("Hello ")
b = MyString("World")
(a + b).m()

更新:您上次使用super的版本适用于我