5

を使用せずに__get__経由__iadd__呼び出すときに、私は次の動作に出くわし両方のケースが同じように動作することを期待してください。さらに、参照カウントとオブジェクトの存続期間についての私の理解から、私はどちらの場合でもオブジェクトを解放する必要があると確信していました。メモリリークが<code>weakref</code>を使用するデコレータ<em>ない</em>を変更しようとして一時的

私はpython2.7とpython3.3の両方でテストしました。

これはバグですか、意図的な動作ですか? 両方の呼び出しで期待される結果が得られる(問題のオブジェクトを解放する)方法はありますか?

私はこれがバウンドメソッドの正しいオブジェクトの有効期間のセマンティクスを破壊するためproxyweakrefを使用したくない:

a = A() 
descr = a.descr 
del a # a is kept alive since descr is a bound method to a 
descr() # should execute a.descr() as expected 

答えて

5

2つのコードパスが等価ではありません。

2つのオペレータにインプレースアドバンテージが割り当てられ、割り当て対象とアイテムが追加されました。 test1では、ローカル変数tempであること、およびインプレース加算は、次に翻訳されています

temp = temp.__iadd__(object()) 

、あなたがselfを返し、tempが同じオブジェクトを参照しているので、これはtemp = tempになり、その参照がクリーンアップされます関数が終了した後test2

、あなたの複雑な事項は、今以降の記述子が再び巻き込まれています。

a.descr += object() 

は次のようになります。

a.descr = A.__dict__['descr'].__get__(a, A).__iadd__(object()) 

ので、あなたはインスタンス属性a.descrA.__dict__['descr'].__get__(a, A)の結果を割り当てます。記述子には__set__()メソッドがなく、相談されません。

しかし、ここではキャッチされ、proxyオブジェクトはaへの参照自体を保持し、a.descr.instanceaへの参照です! 循環参照を作成しました。

この参照は、弱参照を表示するのに十分な長さのオブジェクトを保持しますが、ガベージコレクションプロセスが実行され、このサイクルが破棄されるとすぐにaが削除されます。

このストーリーのモラル? __iadd__を非データ記述子と組み合わせて使用​​しないでください。結果が割り当てられたときの処理を制御する必要があるため、__get____set__の両方を含める必要があります。

+0

実際、 'is_leaky'に' gc.collect() 'を追加すると循環参照が壊れ、' is_leaky(test2) 'がFalseを返します。 – unutbu

+0

はい、test2をa.descr .__ iadd __(object())に変更すると2番目のFalseになります。 – BartoszKP

+1

WOW。これは微妙です。ディスクリプタにNOP '__set__'を追加すると、問題が修正されます。どうもありがとうございました! – coldfix

関連する問題

 関連する問題