Pythonでdecorator


@decoratorは関数機能の動的増加を実現できますが、@decoratorの「改造」後の関数は、元の関数に比べて機能が少し多い以外に、他に異なるところはありませんか?
栗1:decoratorがない場合、関数名を印刷:
def f1(x):
    pass
print(f1.__name__)

結果:f 1
栗2:decoratorがある場合、関数名を印刷:
def log(f):
    def wrapper(*args, **kw):
        print('call...')
        return f(*args, **kw)
    return wrapper

@log
def f2(x):
    pass
print(f2.__name__)

結果:wrapper
以上の栗から,関数名が元の関数f 2ではなく,アクセラレータ関数内部で定義されたwrapperが返され,関数名に依存するコードには失効することが分かる.decoratorは関数の__も変更しましたdoc__などのフォントスタイルや効果を適用します.呼び出し元が@decoratorの「改造」を経た関数が1つも見えないようにするには、元の関数のいくつかの属性を新しい関数にコピーする必要があります.
def log(f):
    def wrapper(*args, **kw):
        print('call...')
        return f(*args, **kw)
    wrapper.__name__ = f.__name__
    wrapper.__doc__ = f.__doc__
    return wrapper

@log
def f2(x):
    pass
print(f2.__name__)

結果:f 2
However、このようにdecoratorを書くのは不便です.元の関数のすべての必要な属性を新しい関数に1つずつコピーするのは難しいので、Pythonに内蔵されているfunctoolsはこの「コピー」のタスクを自動化することができます.
import functools
def log(f):
    @functools.wraps(f)
    def wrapper(*args, **kw):
        print('call...')
        return f(*args, **kw)
    return wrapper

@log
def f2(x):
    pass
print(f2.__name__)

結果:f 2
タスク:前節コードに,@functools.wrapsは、元の関数のプロパティが参照時に変化しないようにします.
import time, functools
from functools import reduce

def performance(unit):
    def perf_decorator(f):
        @functools.wraps(f)
        def wrapper(*args, **kw):
            print('call'+' '+'%s() in %s%s' % (f.__name__, time.time(), unit))
        return wrapper
    return perf_decorator

@performance('ms')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

print(factorial.__name__)


結果:factorial
注:カッコに元の関数名がない場合@functools.wraps(f)は、次のようにエラーを報告します.
AttributeError: ‘functools.partial’ object has no attribute ‘name’
がんばって!学習リンク:https://www.imooc.com/code/6067