2016-05-08 5 views
3

私はPython 3.5.1を使用しています。 私はライブラリを作成しました。特定の状況(ファイルが直接実行されたとき、つまり__name__ == '__main__')では、クラスの1つに特定のメソッドをデコレートしたいと考えています。これまで作成することができるすべてのインスタンスを装飾する必要があります。私はそれが非侵襲的なやり方でやりたい、つまり理想的には私の図書館のクラスは特別なコードを必要としないでしょう。Pythonでクラスのすべてのインスタンスにパッチを適用する

def patch(clazz, name, replacement): 

    def wrap_original(orig): 
     # when called with the original function, a new function will be returned 
     # this new function, the wrapper, replaces the original function in the class 
     # and when called it will call the provided replacement function with the 
     # original function as first argument and the remaining arguments filled in by Python 

     def wrapper(*args, **kwargs): 
      return replacement(orig, *args, **kwargs) 

     return wrapper 

    orig = getattr(clazz, name) 
    setattr(clazz, name, wrap_original(orig)) 

def replacement_function(orig, self, ... other argumnents ...): 
    # orig here is the original function, so can be called like this: 
    # orig(self, ... args ...) 
    pass 

patch(mylib.MyClass, 'method_name', replacemment_function) 

驚くべきことに、私はまだクラスメソッドでそれをテストしていないが、このコードは、動作しますが、私はドン」:しばらく

は、私は私の要件を満たしている。このような何かを実装するために管理しましたそれは今必要です。また、パッチの前に作成されたインスタンスにパッチを当てますが、それが良いかどうかはまだ分かりませんが、d

上記のコードは間違いなく難しいですが、私は書いた後、それは、説明のコメントを書くために。私はもっ​​と簡単なことが大好きです。

質問:Pythonライブラリには、このようなコードが不要になり、既に実装しているものが実装されていますが、それより優れていますか?

+0

これは私が[ここ](https://github.com/host-anshu/simpleInterceptor/blob/master/interceptor.py)のやり方に似ています。私はまた、もっと簡単なものを考え出すことができませんでした。 –

答えて

0

ひとつ、悲しいことに、削除彼女/彼の投稿は、functoolsモジュールの方に私を向けました。最後に、私は次のように落ち着い:partialmethodの結果は、それがであるインスタンスへの最初の引数が、この場合の第2を結合して、

def replacement(self, orig, ... other arguments ...): 
    # orig here is the original function, so can be called like this: 
    # orig(self, ... args ...) 
    pass 

mylib.MyClass.my_method = functools.partialmethod(replacement, mylib.MyClass.my_method) 

場所を切り替えるために必要なorigself引数を意志元の関数(partialmethodの2番目の引数)です。もっときれいに見えます。

1

あなたのアプローチは、それについて行く最もPythonの方法と思われます。

あなたが説明したのとほぼ同じ方法で、Monkey patchingを使用する人気のあるライブラリGevent。performs monkey patching

3

インスタンスで検索すると、が動的に作成されます。インスタンスにはすべてのメソッドのコピーがありません。代わりに、descriptor protocolはクラスから関数を取り込み、必要に応じてそれらをインスタンスにバインドします。これが、クラスのMonkeypatchingがここで動作する理由です。 instance.method_nameは、属性検索がの場合にmylib.MyClass.method_nameとなります。

異なるコードで、以前のメソッドに委譲を処理するさまざまなパターンが必要な場合があるため、ここで行っていることを実行するものは、デフォルトライブラリには何もありません。

あなたのアプローチは、元のものがラッパーに渡されるという点で、関数ラッピングをサポートしています。how the Mercurial projectに非常に近いように見えます。

0

別の方法としては、「ヌル」デコレータ関数を作成し、次にあなたの条件ロジック使用して、その機能と「リアル」のデコレータを切り替えるには、次のようになります。ここではポスターの

from decorator_lib import real_decorator 

def fake_decorator(fun): 
    return fun 

if __name__ == '__main__': 
    my_decorator = real_decorator 
else: 
    my_decorator = fake_decorator 


# ... elsewhere in the module ... 

@my_decorator 
def method(self, a, b, c): 
    pass 

# ... finally: 
if __name__ == '__main__': 
    run_self_tests_or_whatever() 
+0

これは、装飾が行われる前にスワッピングを実行できる場合にのみ機能します。問題が示唆しているようにクラスが既に存在する(そしてインスタンスを持っている)場合、事実の後にデコレータを変更することで何も修正されません。 – Blckknght

+0

OPはmainとして実行するときにメソッドをデコレートしたいと言っています。それは私があなたが前もって扱うことができるものであるように思えます。 –

+0

これでライブラリコードを変更する必要はありませんか?また、ライブラリと装飾を実行するファイルは別々であり、デコレートするクラスはメインコードが実行される前に定義されます。私はこれが私のために働くとは思わない、または私はあなたの提案を誤解した。 – wujek

関連する問題