3

说我有这门课:

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 回答 2

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不提供以及 python 执行操作码(执行操作码的操作码)__radd__时发生了什么的解释。BINARY_ADD+

的缺失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'

如您所见MyString.__add__,没有调用它,但是如果我们交换参数:

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

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

答案在文档中,其中指出:

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

上一项的例外:如果左操作数是内置类型或新样式类的实例,而右操作数是该类型或类的适当子类的实例并覆盖基类的__rop__()方法,则右操作数是该类型或类的适当子类的实例操作数的__rop__()方法在左操作数的__op__()方法之前尝试。

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

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

于 2012-11-12T13:05:25.067 回答
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对我有用

于 2012-11-12T12:46:45.260 回答