項(xiàng)目地址:https://git.io/pytips
Python 的修飾器是一種語(yǔ)法糖(Syntactic Sugar),也就是說(shuō):
@decorator @wrap def func(): pass
是下面語(yǔ)法的一種簡(jiǎn)寫(xiě):
def func(): pass func = decorator(wrap(func))
關(guān)于修飾器的兩個(gè)主要問(wèn)題:
修飾器用來(lái)修飾誰(shuí)
誰(shuí)可以作為修飾器
修飾函數(shù)修飾器最常見(jiàn)的用法是修飾新定義的函數(shù),在 0x0d 上下文管理器中提到上下文管理器主要是為了更優(yōu)雅地完成善后工作,而修飾器通常用于擴(kuò)展函數(shù)的行為或?qū)傩裕?/p>
def log(func): def wraper(): print("INFO: Starting {}".format(func.__name__)) func() print("INFO: Finishing {}".format(func.__name__)) return wraper @log def run(): print("Running run...") run()
INFO: Starting run Running run... INFO: Finishing run修飾類(lèi)
除了修飾函數(shù)之外,Python 3.0 之后增加了對(duì)新定義類(lèi)的修飾(PEP 3129),但是對(duì)于類(lèi)別屬性的修改可以通過(guò) Metaclasses 或繼承來(lái)實(shí)現(xiàn),而新增加的類(lèi)別修飾器更多是出于 Jython 以及 IronPython 的考慮,但其語(yǔ)法還是很一致的:
from time import sleep, time def timer(Cls): def wraper(): s = time() obj = Cls() e = time() print("Cost {:.3f}s to init.".format(e - s)) return obj return wraper @timer class Obj: def __init__(self): print("Hello") sleep(3) print("Obj") o = Obj()
Hello Obj Cost 3.005s to init.類(lèi)作為修飾器
上面兩個(gè)例子都是以函數(shù)作為修飾器,因?yàn)楹瘮?shù)才可以被調(diào)用(callable) decorator(wrap(func))。除了函數(shù)之外,我們也可以定義可被調(diào)用的類(lèi),只要添加 __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 ! LOG: Baking Tag ! LOG: Baking Tag!Hello傳遞參數(shù)在實(shí)際使用過(guò)程中,我們可能需要向修飾器傳遞參數(shù),也有可能需要向被修飾的函數(shù)(或類(lèi))傳遞參數(shù)。按照語(yǔ)法約定,只要修飾器 @decorator 中的 decorator 是可調(diào)用即可,decorator(123) 如果返回一個(gè)新的可調(diào)用函數(shù),那么也是合理的,上面的 @HTML("html") 即是一例,下面再以 flask 的路由修飾器為例說(shuō)明如何傳遞參數(shù)給修飾器:
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! {"/":, "/home": } 向被修飾的函數(shù)傳遞參數(shù),要看我們的修飾器是如何作用的,如果像上面這個(gè)例子一樣未執(zhí)行被修飾函數(shù)只是將其原模原樣地返回,則不需要任何處理(這就把函數(shù)當(dāng)做普通的值一樣看待即可):
@route("/login") def login(user = "user", pwd = "pwd"): print("DB.findOne({{{}, {}}})".format(user, pwd)) login("hail", "python")DB.findOne({hail, python})如果需要在修飾器內(nèi)執(zhí)行,則需要稍微變動(dòng)一下:
def log(f): def wraper(*args, **kargs): print("INFO: Start Logging") f(*args, **kargs) print("INFO: Finish Logging") return wraper @log def run(hello = "world"): print("Hello {}".format(hello)) run("Python")INFO: Start Logging Hello Python INFO: Finish Loggingfunctools由于修飾器將函數(shù)(或類(lèi))進(jìn)行包裝之后重新返回:func = decorator(func),那么有可能改變?cè)竞瘮?shù)(或類(lèi))的一些信息,以上面的 HTML 修飾器為例:
@HTML("body") def body(): """ return body content """ return "Hello, body!" print(body.__name__) print(body.__doc__)LOG: Baking Tag !None 因?yàn)?body = HTML("body")(body) ,而 HTML("body").__call__() 返回的是一個(gè) lambda 函數(shù),因此 body 已經(jīng)被替換成了 lambda,雖然都是可執(zhí)行的函數(shù),但原來(lái)定義的 body 中的一些屬性,例如 __doc__/__name__/__module__ 都被替換了(在本例中__module__沒(méi)變因?yàn)槎荚谕粋€(gè)文件中)。為了解決這一問(wèn)題 Python 提供了 functools 標(biāo)準(zhǔn)庫(kù),其中包括了 update_wrapper 和 wraps 兩個(gè)方法(源碼)。其中 update_wrapper 就是用來(lái)將原來(lái)函數(shù)的信息賦值給修飾器中返回的函數(shù):
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 return body content!有趣的是 update_wrapper 的用法本身就很像是修飾器,因此 functools.wraps 就利用 functools.partial(還記得函數(shù)式編程中的偏應(yīng)用吧?。⑵渥兂梢粋€(gè)修飾器:
from functools import update_wrapper, partial def my_wraps(wrapped): return partial(update_wrapper, wrapped=wrapped) def log(func): @my_wraps(func) def wraper(): print("INFO: Starting {}".format(func.__name__)) func() print("INFO: Finishing {}".format(func.__name__)) return wraper @log def run(): """ Docs" of run """ print("Running run...") print(run.__name__) print(run.__doc__)run Docs" of run
歡迎關(guān)注公眾號(hào) PyHub!
參考Python修飾器的函數(shù)式編程
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/37841.html
摘要:項(xiàng)目地址中的函數(shù)式編程函數(shù)式編程英語(yǔ)或稱函數(shù)程序設(shè)計(jì),又稱泛函編程,是一種編程范型,它將電腦運(yùn)算視為數(shù)學(xué)上的函數(shù)計(jì)算,并且避免使用程序狀態(tài)以及易變對(duì)象。 項(xiàng)目地址:https://git.io/pytips Python 中的函數(shù)式編程 函數(shù)式編程(英語(yǔ):functional programming)或稱函數(shù)程序設(shè)計(jì),又稱泛函編程,是一種編程范型,它將電腦運(yùn)算視為數(shù)學(xué)上的函數(shù)計(jì)算,并且...
摘要:項(xiàng)目地址迭代器與生成器迭代器與生成器是中比較常用又很容易混淆的兩個(gè)概念,今天就把它們梳理一遍,并舉一些常用的例子。生成器前面說(shuō)到創(chuàng)建迭代器有種方法,其中第三種就是生成器。 項(xiàng)目地址:https://git.io/pytips 迭代器與生成器 迭代器(iterator)與生成器(generator)是 Python 中比較常用又很容易混淆的兩個(gè)概念,今天就把它們梳理一遍,并舉一些常用的例...
摘要:看了這一章,發(fā)現(xiàn)原來(lái)是裝飾器,又一新知識(shí)。期間,裝飾器會(huì)做一些額外的工作。書(shū)中介紹了模塊中的三個(gè)裝飾器。另一個(gè)是,這個(gè)裝飾器把函數(shù)結(jié)果保存了起來(lái),避免傳入相同參數(shù)時(shí)重復(fù)計(jì)算。疊放不奇怪,裝飾器返回的就是函數(shù)或可調(diào)用對(duì)象。 在 Web 框架 Flask 中,最??吹降幕蛟S是以@app.route開(kāi)頭的那行代碼。由于還是剛接觸 Flask,所以對(duì)這種語(yǔ)法還不熟悉??戳诉@一章,發(fā)現(xiàn)原來(lái)是裝飾...
摘要:項(xiàng)目地址引入了語(yǔ)句與上下文管理器類(lèi)型,其主要作用包括保存重置各種全局狀態(tài),鎖住或解鎖資源,關(guān)閉打開(kāi)的文件等。了解了語(yǔ)句的執(zhí)行過(guò)程,我們可以編寫(xiě)自己的上下文管理器。生成器的寫(xiě)法更簡(jiǎn)潔,適合快速生成一個(gè)簡(jiǎn)單的上下文管理器。 項(xiàng)目地址:https://git.io/pytips Python 2.5 引入了 with 語(yǔ)句(PEP 343)與上下文管理器類(lèi)型(Context Manager ...
摘要:中的枚舉類(lèi)型枚舉類(lèi)型可以看作是一種標(biāo)簽或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期月份狀態(tài)等。 Python 中的枚舉類(lèi)型 枚舉類(lèi)型可以看作是一種標(biāo)簽或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期、月份、狀態(tài)等。Python 的原生類(lèi)型(Built-in types)里并沒(méi)有專(zhuān)門(mén)的枚舉類(lèi)型,但是我們可以通過(guò)很多方法來(lái)實(shí)現(xiàn)它,例如字典、類(lèi)等: WEEKD...
閱讀 2208·2021-09-27 14:04
閱讀 1944·2019-08-30 15:55
閱讀 1767·2019-08-30 13:13
閱讀 1132·2019-08-30 13:07
閱讀 2802·2019-08-29 15:20
閱讀 3297·2019-08-29 12:42
閱讀 3399·2019-08-28 17:58
閱讀 3679·2019-08-28 17:56