2012-02-15 13 views
5

次のことが妥当かどうか、より良いアプローチがあるかどうかについて意見を求めるだけです。基本的には、__call__を実装する関数またはクラスに適用されるデコレータが必要です。クラスOR関数用のPythonデコレータ

__call__を明示的に装飾するだけでよく、デコレータはクラス定義内に隠されていて、それほど明白ではありません。たぶん私はもっと簡単な解決策を見逃しているかもしれません。

import types 
from functools import wraps 

class dec: 
    """ Decorates either a class that implements __call__ 
     or a function directly. 
    """ 
    def __init__(self, foo): 
     self._foo = foo 

    def __call__(self, target): 
     wraps_class = isinstance(target, types.ClassType) 
     if wraps_class: 
      fun = target.__call__ 
     else: 
      fun = target 

     @wraps(fun) 
     def bar(*args, **kwds): 
      val = args[1] if wraps_class else args[0] 
      print self._foo, val 
      return fun(*args, **kwds) 
     if wraps_class: 
      target.__call__ = bar 
      return target 
     else: 
      return bar 

@dec('A') 
class a: 
    # you could decorate here, but it seems a bit hidden 
    def __call__(self, val): 
     print "passed to a:", val 

@dec('B') 
def b(val): 
    print "passed to b:", val 

a()(11) 
b(22) 

答えて

4

、私は2つのデコレータにこれを分割します:常に機能をラップ1:

def func_dec(foo, is_method=False): 
    def wrapper(fun): 
     @wraps(fun) 
     def bar(*args, **kwds): 
      val = args[1] if is_method else args[0] 
      print foo, val 
      return fun(*args, **kwds) 
     return bar 
    return wrapper 

そして、それは__call__方法を変更したり、単に機能をラップする必要があるかどうかを検出すること、他:

def dec(foo): 
    def wrapper(obj): 
     if inspect.isclass(obj): 
      obj.__call__ = func_dec(foo, is_method=True)(obj.__call__) 
      return obj 
     else: 
      return func_dec(foo)(obj) 
    return wrapper 

inspect.isclassは、旧式クラスと新しいスタイルクラスの両方で正しく動作することに注意してください。

1

これはかなりスリックなアイデアです。私にはうまくいくようですが、 "明示的なものは暗黙的なもの"より、直接的に__call__を飾るのはもっと無邪気であるかもしれません。 2つのことをする1つのデコレータを持つことに少しの概念的なオーバーヘッドがあります。

(任意の関数デコレータを取り、二重の機能/クラスデコレータに変換しますデコレータを作るために悪いか良いでしょうかしら...)

3

私は本当にあなたのアプローチが好きではありません。 インスタンスが呼び出された場合は__call__()メソッドが使用されます。代わりにクラスを呼び出すと__init__()が呼び出されるので、これは実際には類似しているとは思われません。

デコレータは、(直接的または間接的にobjectから派生した)新しいスタイルのクラスでは機能しません。あなた自身が好意を持って、単にこれが欲しいものならば__call__()を飾ります。または、インスタンスのクラスを作成してデコレートするファクトリ関数を記述します。これは、インスタンスを直接呼び出し可能で、selfパラメータを使用してarounfを混乱させる必要がないため、関数のデコレートに似ています。

個人的に
+0

ありがとうございました。 (誰か興味があれば、DzinXはinspect.isclassでチェックすることで新しいスタイルのクラスで動作するように修正しています)。 –

関連する問題