bar
デコレータを使用して動的に作成したいこのコードについて考えてみましょう。
def foo():
def bar():
print "I am bar from foo"
print bar()
def baz():
def bar():
print "I am bar from baz"
print bar()
デコレータを使って外側からバーを作成できると思いました。
def bar2():
print "I am super bar from foo"
setattr(foo, 'bar', bar2)
しかし、結果は私が期待していたものではありません(私は取得したいI am super bar from foo
:
>>> foo()
I am bar from foo
デコレータで既存の関数のサブ関数をオーバーライドすることは可能ですか?
私はライブラリのラッパーを書いています。ボイラープレートコードを避けるために、作業を簡素化したいと思います。
各ライブラリ関数にはプレフィックスがlib_
あり、エラーコードを返します。現在の関数にプレフィックスを追加して、エラーコードを処理したいと思います。これはこれと同じくらい簡単かもしれません:
def call():
fname = __libprefix__ + inspect.stack()[1][3]
return_code = getattr(__lib__, fname)(*args)
if return_code < 0: raise LibError(fname, return_code)
def foo():
call()
問題は、特定の場合に呼び出しの動作が異なる可能性があることです。一部のライブラリ関数はerror_codeを返さないため、次のように記述する方が簡単です。
def foo():
call(check_status=True)
または私の意見でははるかに良いです(これは私がデコレータについて考え始めたポイントです):
@LibFunc(check_status=True)
def foo():
call()
この最後の例では、デコレータ自体によって動的に作成されたサブ関数としてcall
内部foo
を宣言する必要があります。
アイデアは次のようなものを使用することでした:
class LibFunc(object):
def __init__(self,**kwargs):
self.kwargs = kwargs
def __call__(self, original_func):
decorator_self = self
def wrappee( *args, **kwargs):
def call(*args):
fname = __libprefix__ + original_func.__name__
return_code = getattr(__lib__, fname)(*args)
if return_code < 0: raise LibError(fname, return_code)
print original_func
print call
# <<<< The part that does not work
setattr(original_func, 'call', call)
# <<<<
original_func(*args,**kwargs)
return wrappee
最初はcall
、書き込みを最小限に抑えるために、デコレータ自体の内部を呼び出したくなりました。
@LibFunc():
foo(): pass
残念ながら、これはオプションではありません。他のことをcall
:の前後に行う必要があるためです。
@LibFunc():
foo(a,b):
value = c_float()
call(a, pointer(value), b)
return value.value
私が考えたもう1つのオプションはを使用することSWIG
でしたが、SWIGラッピング関数を使用して既存のライブラリを再構築する必要があるため、これもオプションではありません。
最後になりましたが、SWIG
タイプマップからインスピレーションを得て、ラッパーを次のように宣言する場合があります。
@LibFunc(check_exit = true, map = ('<a', '>c_float', '<c_int(b)')):
foo(a,b): pass
これは私にとって最善の解決策のように見えますが、これは別のトピックであり、別の質問です...
あなたはデコレータのアイデアと結婚していますか?あなたの目標がそれぞれがラップするモジュールレベルの関数の束である場合、somelib.lib_somefunctionname
なぜそれが必要なのかわかりません。
これらのモジュールレベルの名前は関数である必要はなく、呼び出し可能である必要があります。それらが__call__
メソッドを持っている限り、それらはクラスインスタンスの束である可能性があります。
2つの異なるサブクラスを使用して、戻り値の処理方法を決定しました。
#!/usr/bin/env python3
import libtowrap # Replace with the real library name.
class Wrapper(object):
'''
Parent class for all wrapped functions in libtowrap.
'''
def __init__(self, name):
self.__name__ = str(name)
self.wrapped_name = 'lib_' + self.__name__
self.wrapped_func = getattr(libtowrap, self.wrapped_name)
self.__doc__ = self.wrapped_func.__doc__
return
class CheckedWrapper(Wrapper):
'''
Wraps functions in libtowrap that return an error code that must
be checked. Negative return values indicate an error, and will
raise a LibError. Successful calls return None.
'''
def __call__(self, *args, **kwargs):
error_code = self.wrapped_func(*args, **kwargs)
if error_code < 0:
raise LibError(self.__name__, error_code)
return
class UncheckedWrapper(Wrapper):
'''
Wraps functions in libtowrap that return a useful value, as
opposed to an error code.
'''
def __call__(self, *args, **kwargs):
return self.wrapped_func(*args, **kwargs)
strict = CheckedWrapper('strict')
negative_means_failure = CheckedWrapper('negative_means_failure')
whatever = UncheckedWrapper('whatever')
negative_is_ok = UncheckedWrapper('negative_is_ok')
モジュールのインポート中にラッパーの「関数」が割り当てられることに注意してください。それらはトップレベルのモジュール名前空間にあり、テストによって隠されていませんif __name__ == '__main__'
。
ほとんどの目的で関数のように動作しますが、わずかな違いがあります。たとえば、各インスタンスに、__name__
割り当てられている名前に一致するaを指定しましたが、...でlib_
使用されているプレフィックス名ではありませんがlibtowrap
、元のをコピーしました__doc__
。これは、のようなプレフィックス名を参照している可能性がありますlib_some_other_function
。また、それらをテストすると、isinstance
おそらく人々を驚かせるでしょう。
デコレータについての詳細、および用のために多くの私は上記のもののようなより多くの迷惑少し矛盾、グラハムDumpletonの半時間の講義「を参照してください。デコレータを作成するための高度な方法」(PyCon米国2014 ;スライド)。彼はwrapt
モジュール(Python Package Index ; Git Hub ; Read the Docs)の作成者であり、通常のデコレータの不整合をすべて(?)修正します。それはあなたの問題を完全に解決するかもしれません(にlib_
現れる古いスタイルの名前を除いて__doc__
)。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加