2016-11-03 8 views
2

__iadd__()メソッドを定義したタイプの読み取り専用プロパティで+=演算子を使用しているときに、Can't set attributeエラーが発生します。セッターを持たないプロパティで+ =演算子を使用するにはどうすればよいですか?

簡体(しかし、実行可能な)私のコードのバージョン:

class Emitter(list): 
    def __iadd__(self, other): 
     self.append(other) 
     return self 

class Widget: 
    def __init__(self): 
     self._on_mouseenter = Emitter() 

    @property 
    def on_mouseenter(self): return self._on_mouseenter 


my_widget = Widget() 
my_widget.on_mouseenter += lambda source: print("on_mouseenter!") 

最後の行がエラーを生成します。

@on_mouseenter.setter 
def on_mouseenter(self, value): pass 

(Runnableをhttps://repl.it/EONf/0で)

この動作は、2つのアカウントに奇妙なようだ:私はWidgetの定義に次の行を追加する場合には消えます。まず、Pythonがオブジェクトを参照渡しすると思ったので、なぜプロパティを読み込み可能にする必要がありますか?そして第二に、私のダミーセッターはどういう仕組みになっていますか?

+0

答えは問題を説明していますが、 'self.on_mouseenter = Emitter()'を直接定義してそのプロパティを避けることができます。 –

+0

用語には注意してください。 Pythonはオブジェクトへの参照を渡しますが、「参照渡し」は微妙に異なるものを指します。 Pythonがしていることは実際には良い標準名を持っていません。 「オブジェクトで呼び出す」「共有で呼び出す」とか、Javaの人々が「値渡しだが渡される値は参照」と呼ぶ人もいます。 – user2357112

+0

@BenHoyt:プロパティを読み取り専用にして、ユーザーコードで上書きできないようにしたかったのです。それはPythonの反パターンですか? – JPNotADragon

答えて

3

__iadd__は、変数にリバウンドする置換オブジェクトを返します。これはもちろんセッターが必要です。

この場合は、セットを無視しているにもかかわらず元のオブジェクトをそのまま残しているため、元のオブジェクトがそのまま残ります。

一部のオブジェクトが不変なので、この動作が必要ですが、その場所にaddはまだ動作しています。

i += 5は、iがバインドされている番号を取り、5を加算し、iを新しい結果番号にリバインドします。つまり、割り当てがあるi = i + 5とまったく同じです。

+0

ありがとう@Max。私の解決策は正しいと言いますか? – JPNotADragon

+0

実際には、渡された値を代入する必要があります。もし '__iadd__'が自己を返さなければ、非常に驚​​くべき結果が得られるかもしれません。 – Max

+0

+ =を使用して追加を行うことは、特にpythonicではありません。人々は+ =ではなく.append()を使ってリストを扱うことに慣れています。 – Max

3

これはPythonのaugmented assignment operatorsがどのように動作するかによって発生します。適切なspecial methodを呼び出した後、オペレータの左側のオブジェクトに戻り値を割り当てます。

xは クラスのインスタンスは__iadd__()方法であれば、x += yx = x.__iadd__(y)と等価です。そうでなければ、x + yの評価と同様に、x.__add__(y)およびy.__radd__(x)は、 と考えられます。

したがって

my_widget.on_mouseenter += lambda source: print("on_mouseenter!") 

my_widget.on_mouseenter = my_widget.on_mouseenter.__iadd__(lambda source: print("on_mouseenter!")) 

に相当し、あなたは割り当てを実行するためのセッターが必要です。しかし、何もする必要はありません。__iadd__メソッドが定義されており、リストをインプレースで変更するためです。

+2

詳細については、Python FAQの[この回答](https://docs.python.org/3.5/faq/programming.html#faq-augmented-assignment-tuple-error)を参照してください。 –

+0

ありがとう、本当に有益でした。 – JPNotADragon

+0

@vaultah:Maxの回答は、それが2番目ほど早いので、受け入れました。お二人のおかげです。 – JPNotADragon

関連する問題