PyTips 0x0f

WBOY
WBOYオリジナル
2016-06-24 11:22:451153ブラウズ

プロジェクトアドレス: https://git.io/pytips

Python の修飾子は一種の糖衣構文です。つまり:

@decorator@wrapdef func():    pass

は次の文法の略語です:

def func():    passfunc = decorator(wrap(func))

に関する 2 つの主な質問デコレータ:

  1. デコレータは誰を使用しますか?

  2. デコレータとして使用できるのは誰ですか? コンテキスト マネージャでは、主に

    後の作業をよりエレガントに完了するために使用されますが、デコレータは通常、関数の動作またはプロパティを拡張します:
  3. 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
クラスを変更する

Python 3.0 以降では、関数の装飾に加えて、新しく定義されたクラスへの変更が追加されました (PEP 3129) が、カテゴリ属性への変更はメタクラスまたは継承を通じて実現できます。新しく追加されたカテゴリ デコレータは、Jython と IronPython を考慮したものですが、構文は同じです。

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.
デコレータとしてのクラス

関数を呼び出すことができるため、上記の 2 つの例は両方ともデコレータとして関数を使用しています。 callable) デコレータ(wrap(func)) 。関数に加えて、呼び出し可能なクラスを定義することもできます。__call__ メソッドを追加するだけです:

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>

パラメータを渡す

実際の使用では、デコレータにパラメータを渡す必要がある場合があります。関数 (またはクラス) はパラメータを渡します。構文規則によれば、デコレータ @decorator 内のデコレータが呼び出し可能である限り、decorator(123) が新しい呼び出し可能な関数を返すのは妥当です。上記の @HTML('html') を例に挙げます。デコレータにパラメータを渡す方法を示す例として、flask のルート デコレータを使用します。

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})

デコレータ内で実行する必要がある場合は、少し変更する必要があります:

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

functools

デコレータは関数 (またはクラス) をラップし、それを再度返します: func =decorator(func)。上記の HTML デコレータを例にとると、元の関数 (またはクラス) の一部の情報を変更することができます。 body = HTML("body")(body)、HTML("body").__call__() はラムダ関数を返すため、body はラムダ関数に置き換えられています。これらはすべて実行可能な関数ですが、一部の属性は最初に定義された関数です。 __doc__ / __name__ / __module__ などの本体が置き換えられています (この場合、__module__ はすべて同じファイル内にあるため変更されていません)。この問題を解決するために、Python は、update_wrapper とラップ メソッド (ソース コード) を含む functools 標準ライブラリを提供します。その中で、update_wrapper は、デコレーターで返された関数に元の関数の情報を割り当てるために使用されます:

@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 を使用することです。 (プログラミングの部分適用関数を思い出してください!) それを修飾子に変えます:

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!    

パブリック アカウント PyHub のフォローへようこそ!

リファレンス

Python デコレータを使用した関数型プログラミング

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。