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ここに、なぜ提供されない__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'

それは呼ばれているので、何が起こっているのですか?

答えは、次のように記載されているドキュメントにあります。

オブジェクトxyの場合、最初x.__op__(y)に試行されます。これが実装されていないか、が返される場合はNotImplementedy.__rop__(x)が試行されます。これも実装されていないか、が返されるNotImplemented場合、TypeError 例外が発生します。ただし、次の例外を参照してください。

前の項目の例外:左側のオペランドが組み込み型または新しいスタイルのクラスのインスタンスであり、右側のオペランドがその型またはクラスの適切なサブクラスのインスタンスであり、ベースの__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 に答える