PyTips 0x0f
项目地址: https://git.io/pytips
Python 的修饰器是一种语法糖(Syntactic Sugar),也就是说:
@decorator@wrapdef func(): pass
是下面语法的一种简写:
def func(): passfunc = decorator(wrap(func))
关于修饰器的两个主要问题:
-
修饰器用来修饰谁
-
谁可以作为修饰器
修饰函数
修饰器最常见的用法是修饰新定义的函数,在 0x0d 上下文管理器 中提到上下文管理器主要是为了 更优雅地完成善后工作 ,而修饰器通常用于扩展函数的行为或属性:
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 ),但是对于类别属性的修改可以通过 Metaclasses 或继承来实现,而新增加的类别修饰器更多是出于 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.
类作为修饰器
上面两个例子都是以函数作为修饰器,因为函数才可以被调用(callable) decorator(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 是可调用即可, 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 修饰器为例:
@HTML("body")def body(): """ return body content """ return "Hello, body!"print(body.__name__)print(body.__doc__)
LOG: Baking Tag <body>!<lambda>None
因为 body = HTML("body")(body) ,而 HTML("body").__call__() 返回的是一个 lambda 函数,因此 body 已经被替换成了 lambda ,虽然都是可执行的函数,但原来定义的 body 中的一些属性,例如 __doc__ / __name__ / __module__ 都被替换了(在本例中 __module__ 没变因为都在同一个文件中)。为了解决这一问题 Python 提供了 functools 标准库,其中包括了 update_wrapper 和 wraps 两个方法( 源码 )。其中 update_wrapper 就是用来将原来函数的信息赋值给修饰器中返回的函数:
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!
有趣的是 update_wrapper 的用法本身就很像是修饰器,因此 functools.wraps 就利用 functools.partial (还记得函数式编程中的偏应用吧!)将其变成一个修饰器:
from functools import update_wrapper, partialdef 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@logdef run(): """ Docs' of run """ print("Running run...")print(run.__name__)print(run.__doc__)
run Docs' of run
欢迎关注公众号 PyHub!
参考
-
Python修饰器的函数式编程

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

WebDevelovermentReliesonHtml,CSS和JavaScript:1)HTMLStructuresContent,2)CSSStyleSIT和3)JavaScriptAddSstractivity,形成thebasisofmodernWebemodernWebExexperiences。

HTML、CSS和JavaScript在Web開發中的作用分別是:1.HTML定義網頁結構,2.CSS控製網頁樣式,3.JavaScript添加動態行為。它們共同構建了現代網站的框架、美觀和交互性。

HTML的未來趨勢是語義化和Web組件,CSS的未來趨勢是CSS-in-JS和CSSHoudini,JavaScript的未來趨勢是WebAssembly和Serverless。 1.HTML的語義化提高可訪問性和SEO效果,Web組件提升開發效率但需注意瀏覽器兼容性。 2.CSS-in-JS增強樣式管理靈活性但可能增大文件體積,CSSHoudini允許直接操作CSS渲染。 3.WebAssembly優化瀏覽器應用性能但學習曲線陡,Serverless簡化開發但需優化冷啟動問題。

HTML的未來充滿了無限可能。 1)新功能和標準將包括更多的語義化標籤和WebComponents的普及。 2)網頁設計趨勢將繼續向響應式和無障礙設計發展。 3)性能優化將通過響應式圖片加載和延遲加載技術提升用戶體驗。

HTML、CSS和JavaScript在網頁開發中的角色分別是:HTML負責內容結構,CSS負責樣式,JavaScript負責動態行為。 1.HTML通過標籤定義網頁結構和內容,確保語義化。 2.CSS通過選擇器和屬性控製網頁樣式,使其美觀易讀。 3.JavaScript通過腳本控製網頁行為,實現動態和交互功能。

HTML是構建網頁結構的基石。 1.HTML定義內容結構和語義,使用、、等標籤。 2.提供語義化標記,如、、等,提升SEO效果。 3.通過標籤實現用戶交互,需注意表單驗證。 4.使用、等高級元素結合JavaScript實現動態效果。 5.常見錯誤包括標籤未閉合和屬性值未加引號,需使用驗證工具。 6.優化策略包括減少HTTP請求、壓縮HTML、使用語義化標籤等。

HTML、CSS和JavaScript是構建現代網頁的核心技術:1.HTML定義網頁結構,2.CSS負責網頁外觀,3.JavaScript提供網頁動態和交互性,它們共同作用,打造出用戶體驗良好的網站。

HTMLISNOTAPROGRAMMENGUAGE; ITISAMARKUMARKUPLAGUAGE.1)htmlStructures andFormatSwebContentusingtags.2)itworkswithcsssforstylingandjavascript for Interactivity,增強WebevebDevelopment。
