2011-07-15 30 views
20

PythonのRubyのmethod_missingメソッドと同等のものは何ですか?私は__getattr__を使ってみましたが、このフックもフィールドに適用されます。私はメソッドの呼び出しをインターセプトしたいだけです。それを行うPythonの方法は何ですか?Rubyの 'method_missing'に相当するPython

答えて

27

プロパティとメソッドの間にPythonの違いはありません。メソッドは型がちょうどinstancemethodの単なるプロパティです。このメソッドは呼び出し可能です(__call__をサポートしています)。

__getattr__メソッドを実装したい場合は、関数を返してください(lambdaまたは普通のdef)。コール後に何かをチェックしてください。

+1

ありがとうございます。私はちょっとグーグルで[this](http://venodesigns.net/2010/11/02/emulating-rubys-method_missing-in-python/)を見つけました。 – missingfaktor

+2

参考までに、問題となるリンクは、メソッドとみなすべき属性名のリストを定義することであいまいさを取り除きます(これらのメソッドのそれぞれを_define_してスタブに委譲できるので、目的を少し破るように思えます)。 –

4

PythonはRubyのやり方とは異なり、メソッドと属性(「インスタンス変数」など)を区別しません。メソッドやその他のオブジェクト属性は、Pythonで全く同じ方法で検索されます。Pythonでさえルックアップ段階での違いを知るわけではありません。属性が見つかるまで、それは単なる文字列です。

あなたは__getattr__のみは、メソッドのためと呼ばれていることを確認する方法を求めているのであれば、私はあなたがおそらくエレガントな解決策を見つけることができません怖いです。しかし、__getattr__から関数(またはまったく新しいdynamically bound method)を返すだけで十分です。

+1

をRubyでは 'method_missing'を、スモールトークでは' doesNotUnderstand'を使います。 – missingfaktor

+0

私はなぜあなたが '__getattr__'を使いたいのか理解しています。私はあなたがなぜメソッド呼び出しを傍受したいのか理解できません。 – senderle

+0

私の設計ではフィールドへのアクセスを傍受する必要はありませんでしたが、大きな障害とは思われません。私は@Emilによって提供される解決策が私の要求に十分に適していることを知ります。 – missingfaktor

1

私はそれをお勧めしませんが!!!!!!!!!!!!!!!!!!!!!

このようなことは、呼び出し可能な属性/メソッドに対応しないすべての名前に対して特別なメソッドを呼び出す動作に近づくことになります。もちろん、彼らはまだ本当に別の名前空間を持っていないので、少し奇妙に感じるかもしれません。それはより低いレベルで動作する__getattribute__をオーバーライドして動作します。__getattr__それが失敗した場合は属性を取得しようとします。コールされた特殊メソッドを返します。成功した場合は呼び出し可能な場合はそれを渡します。 は、特殊メソッドで呼び出しを実装する点を除いて、ほぼ同じように動作するプロキシオブジェクトで結果をラップします。

呼び出し元のオブジェクトにアクセスすることはできません。呼び出し元のオブジェクトにアクセスすることは許可されていません。私が考えることができると思うのは、1分後にそれを削除する新しいスレッドを開始することです。その場合、サポートされていないクロージャでそれを使用していない限りおそらく呼び出されます)。

編集:私は忘れてしまったコール可能ないくつかの偽陽性があるかもしれません。

http://pypi.python.org/pypi/ProxyTypesライブラリに依存

from peak.util.proxies import ObjectWrapper 
from functools import partial 

def m(name, *args, **kwargs): 
    print(name,repr(args),repr(kwargs)) 


class CallProxy(ObjectWrapper): 
    def __init__(self, obj, m, method_name): 
     ObjectWrapper.__init__(self, obj) 
     object.__setattr__(self, "_method_name", method_name) 
     object.__setattr__(self, "_m", m) 

    def __call__(self, *args, **kwargs): 
     return self._m(self._method_name, *args,**kwargs) 


class Y(object): 
    def __init__(self): 
     self.x = [3] 
    def __getattribute__(self, name): 
     try: 
      val = object.__getattribute__(self, name) 
      if not callable(val): 
       return CallProxy(val, m, name) 
      else: 
       return val 
     except AttributeError: 
      return partial(m, name) 

In [2]: y=Y() 

In [3]: y.x 
Out[3]: [3] 

In [4]: y.z 
Out[4]: <functools.partial at 0x2667890> 

In [5]: y.zz([12]) 
('zz', '([12],)', '{}') 

In [6]: y.x.append(5) 

In [7]: y.x 
Out[7]: [3, 5] 

In [8]: y.x(1,2,3,key="no") 
('x', '(2, 3)', "{'key': 'no'}") 
あなたは以下の方法で機能のようなmissing_method実装することができ
3

:同じ理由

https://gist.github.com/gterzian/6400170

import unittest 
from functools import partial 

class MethodMissing: 
    def method_missing(self, name, *args, **kwargs): 
     '''please implement''' 
     raise NotImplementedError('please implement a "method_missing" method') 

    def __getattr__(self, name): 
     return partial(self.method_missing, name) 


class Wrapper(object, MethodMissing): 
    def __init__(self, item): 
     self.item = item 

    def method_missing(self, name, *args, **kwargs): 
     if name in dir(self.item): 
      method = getattr(self.item, name) 
      if callable(method): 
       return method(*args, **kwargs) 
      else: 
       raise AttributeError(' %s has not method named "%s" ' % (self.item, name)) 


class Item(object): 
    def __init__(self, name): 
     self.name = name 

    def test(self, string): 
     return string + ' was passed on' 


class EmptyWrapper(object, MethodMissing): 
    '''not implementing a missing_method''' 
    pass 


class TestWrapper(unittest.TestCase): 
    def setUp(self): 
     self.item = Item('test') 
     self.wrapper = Wrapper(self.item) 
     self.empty_wrapper = EmptyWrapper() 

    def test_proxy_method_call(self): 
     string = self.wrapper.test('message') 
     self.assertEqual(string, 'message was passed on') 

    def test_normal_attribute_not_proxied(self,): 
     with self.assertRaises(AttributeError): 
      self.wrapper.name 
      self.wrapper.name() 

    def test_empty_wrapper_raises_error(self,): 
     with self.assertRaises(NotImplementedError): 
      self.empty_wrapper.test('message') 


if __name__ == '__main__': 
    unittest.main()