... デコレータとして使用できる人
デコレータとして使用できる人
関数の変更
、デコレーターは通常、関数の動作やプロパティを拡張するために使用されます:
@decorator@wrapdef func(): pass
def func(): passfunc = decorator(wrap(func))
Python 3.0 では、関数の装飾に加えて、新しく定義されたクラスの装飾が追加されました ( PEP 3129)、ただし、カテゴリ属性の変更はメタクラスまたは継承を通じて実現でき、新しく追加されたカテゴリ デコレータは Jython と IronPython を考慮したものですが、その構文は依然として非常に一貫しています:
def log(func): def wraper(): print("INFO: Starting {}".format(func.__name__)) func() print("INFO: Finishing {}".format(func.__name__)) return wraper@logdef run(): print("Running run...")run()
INFO: Starting runRunning run...INFO: Finishing run
from time import sleep, timedef timer(Cls): def wraper(): s = time() obj = Cls() e = time() print("Cost {:.3f}s to init.".format(e - s)) return obj return wraper@timerclass Obj: def __init__(self): print("Hello") sleep(3) print("Obj")o = Obj()
HelloObjCost 3.005s to init.
パラメータを渡す 実際の使用では、デコレータにパラメータを渡す必要がある場合や、デコレータにパラメータを渡す必要がある場合があります。変更された関数 (またはクラス)。構文規則によれば、デコレータ @decorator 内のデコレータが呼び出し可能である限り、decorator(123) が新しい呼び出し可能な関数を返すのは妥当です。上記の @HTML('html') を例に挙げます。デコレータにパラメータを渡す方法を示す例として、flask のルート デコレータを使用します。
class HTML(object): """ Baking HTML Tags! """ def __init__(self, tag="p"): print("LOG: Baking Tag <{}>!".format(tag)) self.tag = tag def __call__(self, func): return lambda: "<{0}>{1}</{0}>".format(self.tag, func(), self.tag) @HTML("html")@HTML("body")@HTML("div")def body(): return "Hello"print(body())
LOG: Baking Tag <html>!LOG: Baking Tag <body>!LOG: Baking Tag <div>!<html><body><div>Hello</div></body></html>
RULES = {}def route(rule): def decorator(hand): RULES.update({rule: hand}) return hand return decorator @route("/")def index(): print("Hello world!")def home(): print("Welcome Home!")home = route("/home")(home)index()home()print(RULES)
Hello world!Welcome Home!{'/': <function index at 0x10706f730>, '/home': <function home at 0x10706f8c8>}
デコレーター内で実行する必要がある場合は、少し変更する必要があります:
@route("/login")def login(user = "user", pwd = "pwd"): print("DB.findOne({{{}, {}}})".format(user, pwd))login("hail", "python")
DB.findOne({hail, python})
デコレータは関数 (またはクラス) をラップして返します: func =decorator(func)。上記の HTML デコレータを例にとると、元の関数 (またはクラス) の一部の情報を変更できます:
def log(f): def wraper(*args, **kargs): print("INFO: Start Logging") f(*args, **kargs) print("INFO: Finish Logging") return wraper@logdef run(hello = "world"): print("Hello {}".format(hello))run("Python")
INFO: Start LoggingHello PythonINFO: Finish Logging
@HTML("body")def body(): """ return body content """ return "Hello, body!"print(body.__name__)print(body.__doc__)
LOG: Baking Tag <body>!<lambda>None
興味深いのは、update_wrapper 自体の使用法がデコレーターと非常に似ているため、functools.wraps は functools.partial を使用することです。 (プログラミングの関数 Partial application を思い出してください!) それをデコレータに変えます:
from functools import update_wrapper"""functools.update_wrapper(wrapper, wrapped[, assigned][, updated])"""class HTML(object): """ Baking HTML Tags! """ def __init__(self, tag="p"): print("LOG: Baking Tag <{}>!".format(tag)) self.tag = tag def __call__(self, func): wraper = lambda: "<{0}>{1}</{0}>".format(self.tag, func(), self.tag) update_wrapper(wraper, func) return wraper @HTML("body")def body(): """ return body content! """ return "Hello, body!"print(body.__name__)print(body.__doc__)
LOG: Baking Tag <body>!body return body content!
リファレンス