2017-01-27 8 views
5

クラスのバインドされたメソッドを保持する変数がそのメソッドの別のリファレンスと同じかどうかをチェックするテストを作成しようとしています。通常これは問題ではありませんが、同じクラスの別のメソッド内で実行された場合は動作しません。ここでは、最小限の例です:Pythonがバインドされたメソッドを自分自身と比較できない

class TestClass: 
    def sample_method(self): 
     pass 
    def test_method(self, method_reference): 
     print(method_reference is self.sample_method) 

私は本当にassert代わりのprintを使用していますが、最終結果は同じであるので、それはどちらもここにもあります。次のようにテストが実行されます。

instance = TestClass() 
instance.test_method(instance.sample_method) 

結果は、私はそれがTrueであることを期待していていてもFalseです。この問題はPython 3.5とPython 2.7(Anacondaの下で動作しています)の両方で現れます。

バインドされたメソッドは、TestClass.test_method.__get__(instance, type(instance))のようなものを実行することによって取得されるクロージャです。しかし、私はself.sample_methodがすでにそのようなクロージャへの参照であると期待しているので、self.sample_methodinstance.sample_methodは同じ参照を表します。私が正しく出力を理解していれば、実際の比較(、

assert <bound method TestTransformFormatter.transform1 of <matplotlib.tests.test_ticker.TestTransformFormatter object at 0x7f0101077b70>> is <bound method TestTransformFormatter.transform1 of <matplotlib.tests.test_ticker.TestTransformFormatter object at 0x7f0101077b70>> 
E  + where <bound method TestTransformFormatter.transform1 of <matplotlib.tests.test_ticker.TestTransformFormatter object at 0x7f0101077b70>> = <matplotlib.ticker.TransformFormatter object at 0x7f0101077e10>.transform 
E  + and <bound method TestTransformFormatter.transform1 of <matplotlib.tests.test_ticker.TestTransformFormatter object at 0x7f0101077b70>> = <matplotlib.tests.test_ticker.TestTransformFormatter object at 0x7f0101077b70>.transform1 

:ここで私を混乱されているものの

パートは私が実行している本当のpytestテスト(matplotlibためのPRに取り組んで)の出力であります最初の行)は実際に同じオブジェクトを比較していますが、何とかしてFalseになります。この時点で私が想像できる唯一のことは、実際には__get__が2回呼び出されているということですが、理由/方法/方法もわからず、回避する方法もわかりません。

+5

これは完全に予想されます。すべての 'instance.sample_method'が同じオブジェクトであるわけではありません。すべての' 5024 'が同じオブジェクトであるわけではありません。 – user2357112

+0

@ user2357112それは意味をなさない。ここで私がうまくいけば明らかになった私の質問は、実際に異なるオブジェクトがどこに作られているのでしょうか? –

+0

'instance.sample_method'に対して1回、' test_method'内で 'self.sample_method'に対して1回です。 – user2357112

答えて

5

彼らは同じ参照じゃない - の2つのメソッドを表すオブジェクトがメモリ内の別の場所を占有:method_reference == self.sample_methodに変更

>>> class TestClass: 
...  def sample_method(self): 
...   pass 
...  def test_method(self, method_reference): 
...   print(hex(id(method_reference))) 
...   print(hex(id(self.sample_method))) 
... 
>>> instance = TestClass() 
>>> instance.test_method(instance.sample_method) 
0x7fed0cc561c8 
0x7fed0cc4e688 

は、しかし、アサートパスを行います。

質問が拡大されて以来の編集:欠陥のあるテストのようです - おそらくコードの実際の機能は参照が同じ(is)、ちょうど等しい(==)である必要はありません。だからあなたの変更はおそらくテスト以外の何かを壊しませんでした。

+0

'=='が動作することを指摘してくれてありがとう。バインドされていないメソッドの実装はisですが、バインドされたメソッドが実際に同じであるかどうかを実際に確認できるということはわかります。 –

+0

テストは実際に今書かれています。私は正しい考えを持っていただけで、コードで正しく表現する方法を知らなかった。 –

+1

私はもっと徹底的なテストを行いました。メソッドのために '=='が判明しました[本当に変です](http://stackoverflow.com/questions/18216597/how-should-functions-be-tested-for-equality- or-identity/18217024#18217024)、2つのメソッドオブジェクトが同じオブジェクトの同じメソッドを表すかどうかを判断するのには不十分です。 – user2357112

3

受け入れられた答えが間違っているわけではありませんが、メソッドが属性ルックアップにバインドされていることに注意してください。さらに、バインドされていないメソッドの動作は、Python 2.XとPython 3.Xの間で変更されます。

class A: 
    def method(self): 
     pass 

a = A() 
print(a.method is a.method) # False 

print(A.method is A.method) # Python 3.X: True, Python 2.X: False 
+0

後書きのためにここに出てくるディスクリプタの仕組みについて何かを追加できますか? –

+0

また、Py2では、バインドされていないメソッドの例が 'False'を返すのはなぜですか?代わりに '=='で動作しますか? –

関連する問題