Pythonのメソッドデコレータの引数としてクラスインスタンス変数を使用するにはどうすればよいですか?
質問
クラスインスタンス変数をPythonのメソッドデコレータの引数として使用するにはどうすればよいですか? 以下は、私がやろうとしていることを示す最小限の例です。デコレータ関数がインスタンスへの参照にアクセスできないため、明らかに失敗します。デコレータから参照にアクセスする方法がわかりません。
def decorator1(arg1):
def wrapper(function):
print "decorator argument: %s" % arg1
return function
return wrapper
class Foo(object):
def __init__(self, arg1):
self.var1 = arg1
@decorator1(self.var1)
def method1(self):
print "method1"
foo = Foo("abc")
foo.method1()
解決
機能しません。デコレータは、クラスの作成時間中に呼び出されます。これは、インスタンスが作成されるずっと前です(これが発生する場合は if )。あなたの「デコレーター」がインスタンスが必要な場合は、「装飾」を行う必要があります。インスタンス化時:
def get_decorator(arg1):
def my_decorator(function):
print "get_decorator argument: %s" % arg1
return function
return my_decorator
class Foo(object):
def __init__(self, arg1):
self.var1 = arg1
self.method1 = get_decorator(self.var1)(self.method1)
def method1(self):
print "method1"
foo = Foo("abc")
foo.method1()
関数の名前を意味に従って変更したことに注意してください。実際の「デコレータ」、つまりメソッドを(潜在的に)変更する関数は、 decorator1
ではなく、 wrapper
です。
他のヒント
あなたの“ワーパー” functionは、実際にはワーパーではなく、デコレータです。あなたの“ decorator1” functionはデコレータコンストラクタです。実行時にself.var1にアクセスしたい場合は、デコレーターではなくワーパーを作成する必要があります:
def decorator(function):
def wrapper(self,*args,**kwargs):
print "Doing something with self.var1==%s" % self.var1
return function(self,*args,**kwargs)
return wrapper
class Foo(object):
def __init__(self, arg1):
self.var1 = arg1
@decorator
def method1(self):
print "method1"
foo = Foo("abc")
foo.method1()
より一般的なデコレータが必要な場合は、呼び出し可能なクラスを宣言することをお勧めします:
class decorator:
def __init__(self,varname):
self.varname = varname
def __call__(self,function):
varname=self.varname
def wrapper(self,*args,**kwargs):
print "Doing something with self.%s==%s" % (varname,getattr(self,varname))
return function(self,*args,**kwargs)
return wrapper
使用方法:
@decorator("var1")
デコレータはクラスが定義されると実行されるため、インスタンス変数を渡すことはできません。
昔はこれをどのように行っていましたか。
class Foo(object):
def __init__(self, arg1):
self.var1 = arg1
def method1(self):
self.lock()
try:
self.do_method1()
except Exception:
pass # Might want to log this
finally:
self.unlock()
def do_method1(self):
print "method1"
def lock(self):
print "locking: %s" % self.arg1
def unlock(self):
print "unlocking: %s" % self.arg1
今、サブクラスは、 do_method1
をオーバーライドするだけで" wrapping"の利点を得ることができます。 with
ステートメントなしで、古い方法で完了します。
はい、時間がかかります。ただし、魔法も含まれていません。