如何委托超类的__add__方法?

Eri*_*ric 3 python oop inheritance overriding delegation

说我有这个班:

class MyString(str):
    def someExtraMethod(self):
        pass
Run Code Online (Sandbox Code Playgroud)

我希望能够做到

a = MyString("Hello ")
b = MyString("World")
(a + b).someExtraMethod()
("a" + b).someExtraMethod()
(a + "b").someExtraMethod()
Run Code Online (Sandbox Code Playgroud)
  1. 按原样运行:

    AttributeError: 'str' object has no attribute 'someExtraMethod'
    
    Run Code Online (Sandbox Code Playgroud)
  2. 显然这不起作用.所以我补充一下:

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

    def __add__(self, other):
        return MyString(super(MyString, self).__add__(other))
    def __radd__(self, other):
        return MyString(super(MyString, self).__radd__(other))
    
    Run Code Online (Sandbox Code Playgroud)
    AttributeError: 'super' object has no attribute '__radd__'
    
    Run Code Online (Sandbox Code Playgroud)

仍然没有运气.我该怎么办?

Bak*_*riu 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'>
Run Code Online (Sandbox Code Playgroud)

如果你实施,__radd__你得到一个AttributeError(见下面的注释).

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

此外,如果您只想添加单个方法,则可以尝试在纯字符串上使用函数.


我在这里添加了一些解释,说明为什么str不提供__radd__以及当python执行BINARY_ADD操作码(执行操作的操作码)时会发生什么+.

的abscence str.__radd__是由于字符串对象实现序列的并置运算符,而不是数字加法运算的事实.对于诸如list或的其他序列也是如此tuple.这些在"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'
Run Code Online (Sandbox Code Playgroud)

你可以看到MyString.__add__没有调用,但如果我们交换参数:

>>> MyString('test') + 'test'
__add__
'testtest'
Run Code Online (Sandbox Code Playgroud)

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

答案在文件中指出:

对于对象xy,首先x.__op__(y)是尝试.如果没有实现或返回NotImplemented,y.__rop__(x)则尝试.如果这也没有实现或返回NotImplemented,TypeError 则引发异常.但请参阅以下异常:

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

这样做是为了使子类可以完全覆盖二元运算符.否则,左操作数的__op__()方法将始终接受右操作数:当期望给定类的实例时,该类的子类的实例始终是可接受的.

这意味着你必须实现所有的方法str __r*__方法,否则,你仍然有争论订单的问题.