ラップは、実際には、デコレータによって引き起こされる、元の関数名が指す関数のプロパティの変更の問題を解決するために使用されます。
現時点では、デコレータは関数 func を修飾しています。は実際の関数を指しているのではなく、デコレーター内の装飾された関数を指しています
import sys debug_log = sys.stderr def trace(func): if debug_log: def callf(*args, **kwargs): """A wrapper function.""" debug_log.write('Calling function: {}\n'.format(func.__name__)) res = func(*args, **kwargs) debug_log.write('Return value: {}\n'.format(res)) return res return callf else: return func @trace def square(x): """Calculate the square of the given number.""" return x * x
ここでの四角は実際には呼び出しを指しています。help(square) または square.__name__ を使用して確認できます。
装飾された正方形の関数は、実際には別の関数です(関数名などの関数属性が変わります)
装飾にラップを使用する場合
def trace(func): if debug_log: @functools.wraps(func) def callf(*args, **kwargs): """A wrapper function.""" debug_log.write('Calling function: {}\n'.format(func.__name__)) res = func(*args, **kwargs) debug_log.write('Return value: {}\n'.format(res)) return res return callf else: return func
このとき、トレースで装飾された正方形の属性は変わりませんhelp(square) で確認できます
理由: ラップの装飾コードを次のように変換します。同等のものは次のとおりです:
def trace(func): if debug_log: def _callf(*args, **kwargs): """A wrapper function.""" debug_log.write('Calling function: {}\n'.format(func.__name__)) res = func(*args, **kwargs) debug_log.write('Return value: {}\n'.format(res)) return res callf = functools.update_wrapper(_callf, wrapped = func,assigned = functools.WRAPPER_ASSIGNMENTS,updated = functools.WRAPPER_UPDATES) return callf else: return func
update_wrapper は非常に単純なジョブを実行します。これは、ラップされたパラメータで表される関数オブジェクトです (例: square ) 一部の属性 (__name__、__doc__ など) は、パラメーター ラッパーで表される関数オブジェクトの対応する属性をオーバーライドします (例: callf、callf は単純に square 関数を呼び出すため、callf は次のラッパー関数であると言えます)四角)。
したがって、この例で、wraps デコレータを使用して callf を「装飾」した後、callf の __doc__、__name__、およびその他の属性は、トレースによって「装飾」される関数 square の属性とまったく同じになります。