Pythonはどうやって装飾器を作成する時に関数要素の情報を保持しますか?


問題
あなたが書いた装飾器はある関数に作用しますが、この関数の重要な要素情報は名前、文書文字列、注釈、パラメータの署名などがなくなりました。
ソリューション
いつでも装飾器を定義する時は、Fnctoolsライブラリの中の@wraps装飾器を使って、底の包装関数を注釈するべきです。たとえば:

import time
from functools import wraps
def timethis(func):
  '''
  Decorator that reports the execution time.
  '''
  @wraps(func)
  def wrapper(*args, **kwargs):
    start = time.time()
    result = func(*args, **kwargs)
    end = time.time()
    print(func.__name__, end-start)
    return result
  return wrapper
これを包装した後の関数を使って、元の情報を確認します。

>>> @timethis
... def countdown(n):
...   '''
...   Counts down
...   '''
...   while n > 0:
...     n -= 1
...
>>> countdown(100000)
countdown 0.008917808532714844
>>> countdown.__name__
'countdown'
>>> countdown.__doc__
'
\tCounts down
\t' >>> countdown.__annotations__ {'n': <class 'int'>} >>>
討論する
装飾器を作る時にメタ情報をコピーすることは非常に重要な部分です。 @wraps を使用することを忘れたら、装飾関数によって有用な情報が全部無くなりました。例えば@wraps を無視した後の効果は以下のようです。

>>> countdown.__name__
'wrapper'
>>> countdown.__doc__
>>> countdown.__annotations__
{}
>>>
@wrapsには重要な特徴があります。属性を通すことができます。wrapped_uパッケージ関数に直接アクセスします。たとえば:

>>> countdown.__wrapped__(100000)
>>>
同前wrapped_u属性はさらに、装飾関数によって、一階のパラメータ署名情報を正確に露出させることができる。たとえば:

>>> from inspect import signature
>>> print(signature(countdown))
(n:int)
>>>
一つの非常に一般的な問題はどのように飾り器に直接原始関数のパラメータ署名情報をコピーさせるかです。自分で手動で実現するには大量の作業が必要です。下の @wraps 属性を通じて関数署名情報にアクセスする。
以上はPythonがどうやって装飾器を作成する時に関数要素の情報の詳細を保留します。Pythonについては関数要素の情報を保持します。他の関連記事に注目してください。