私は通常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__
は、文字列オブジェクトが数値加算演算ではなく、シーケンスの連結演算子を実装しているためです。list
またはなどの他のシーケンスについても同じことが言えますtuple
。これらは「Pythonレベル」では同じですが、実際にはC構造体に2つの異なる「スロット」があります。
Pythonでは2つの異なるmethods(および)によって定義される数値+
演算子は、実際には、への呼び出しをシミュレートするためにスワップされた引数で呼び出される単一のC関数です。__add__
__radd__
__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'
それは呼ばれているので、何が起こっているのですか?
答えは、次のように記載されているドキュメントにあります。
オブジェクトx
とy
の場合、最初x.__op__(y)
に試行されます。これが実装されていないか、が返される場合はNotImplemented
、y.__rop__(x)
が試行されます。これも実装されていないか、が返されるNotImplemented
場合、TypeError
例外が発生します。ただし、次の例外を参照してください。
前の項目の例外:左側のオペランドが組み込み型または新しいスタイルのクラスのインスタンスであり、右側のオペランドがその型またはクラスの適切なサブクラスのインスタンスであり、ベースの__rop__()
メソッドをオーバーライドする場合、右側のオペランドの__rop__()
メソッドは、左側のオペランドのメソッドの前に試行され__op__()
ます。
これは、サブクラスが二項演算子を完全にオーバーライドできるようにするために行われます。それ以外の場合、左のオペランドの__op__()
メソッドは常に右のオペランドを受け入れます。特定のクラスのインスタンスが予期される場合、そのクラスのサブクラスのインスタンスは常に受け入れられます。
つまり、のすべてのメソッドstr
とメソッドを実装する必要があります。そうしないと、__r*__
引数の順序に問題が発生します。