Python 修饰器与 functools_html/css_WEB-ITnose
@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: "{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传递参数
在实际使用过程中,我们可能需要向修饰器传递参数,也有可能需要向被修饰的函数(或类)传递参数。按照语法约定,只要修饰器 @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!{'/':, '/home': } 向被修饰的函数传递参数,要看我们的修饰器是如何作用的,如果像上面这个例子一样未执行被修饰函数只是将其原模原样地返回,则不需要任何处理(这就把函数当做普通的值一样看待即可):
@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 Loggingfunctools
由于修饰器将函数(或类)进行包装之后重新返回: func = decorator(func),那么有可能改变原本函数(或类)的一些信息,以上面的 HTML修饰器为例:
@HTML("body")def body(): """ return body content """ return "Hello, body!"print(body.__name__)print(body.__doc__)LOG: Baking Tag !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: "{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(还记得函数式编程中的偏应用吧!)将其变成一个修饰器:
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参考
Python修饰器的函数式编程
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
相关文章
相关视频
推荐阅读
-
解析Python中的生成器及其与迭代器的差异
-
一篇文章彻底搞懂Python中可迭代(Iterable)、迭代器(Iterator)与生成器(Generator)的概念
-
python3 property装饰器实现原理与用法示例
-
在Mac OS上使用mod_wsgi连接Python与Apache服务器
-
Python设计模式之解释器模式原理与用法实例分析
-
Python设计模式之迭代器模式原理与用法实例分析
-
Windows下使Python2.x版本的解释器与3.x共存的方法
-
Python3.5迭代器与生成器用法实例分析
-
Python多进程与服务器并发原理及用法实例分析
-
Python的装饰器模式与面向切面编程详解