4

私は 2 つのクラスを持っています。1 つは「インプレース演算子」オーバーライド (たとえば+=) を持ち、もう 1 つは を介し​​て最初のインスタンスを公開します@property。(注:これは、実際のコードから問題を再現する最小限のものまで大幅に簡略化されています。)

class MyValue(object):
    def __init__(self, value):
        self.value = value

    def __iadd__(self, other):
        self.value += other
        return self

    def __repr__(self):
        return str(self.value)

class MyOwner(object):
    def __init__(self):
        self._what = MyValue(40)

    @property
    def what(self):
        return self._what

ここで、公開されたプロパティでその演算子を使用しようとすると:

>>> owner = MyOwner()
>>> owner.what += 2
AttributeError: can't set attribute

プロパティを on に設定しようとしているので、これは予想されることownerです。 プロパティを新しいオブジェクトに設定するのを防ぎながら、その背後にあるオブジェクトを (その場で)変更できるようにする方法はありますか、それとも言語の癖ですか?

(この質問も参照してください。ただし、最終的には Python 3 で動作するようにするため、できれば古いスタイルのクラスに戻さに、別の方法で行こうとしています。)


それまでの間、同じことを行う方法でこれを回避しました。

class MyValue(object):
    # ... 

    def add(self, other):
        self.value += other

>>> owner = MyOwner()
>>> owner.what.add(2)
>>> print(owner.what)
42
4

1 に答える 1

5

これは言語の癖です。操作は次のobject += valueように変換されます。

object = object.__iadd__(value)

これは、すべてのオブジェクトが可変であるとは限らないため必要です。あなたのものであり、正しく返さselfれ、上記の操作の割り当て部分の仮想ノーオペレーションが発生します。

あなたの場合、object問題は属性でもあるため、次が実行されます。

owner.what = owner.what.__iadd__(2)

object.whatここで左側 ( のように)を参照することを避けることとは別に、tmp = owner.what; tmp += 2これをきれいに処理する方法があります。

プロパティへの割り当てが同じオブジェクトに関係していることを簡単に検出し、それをゲートします。

class MyOwner(object):
    def __init__(self):
        self._what = MyValue(40)

    @property
    def what(self):
        return self._what

    @what.setter
    def what(self, newwhat):
        if newwhat is not self._what:
            raise AttributeError("can't set attribute")
        # ignore the remainder; the object is still the same
        # object *anyway*, so no actual assignment is needed

デモ:

>>> owner = MyOwner()
>>> owner.what
40
>>> owner.what = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 24, in what
AttributeError: can't set attribute
>>> owner.what += 2
>>> owner.what
42
于 2016-07-22T16:54:19.603 に答える