博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python装饰器
阅读量:5923 次
发布时间:2019-06-19

本文共 5265 字,大约阅读时间需要 17 分钟。

一.函数装饰器

1.闭包和工厂函数

闭包无非是使内层函数在调用时记住它当前环境的状态。初学者经常认为闭包就是内层函数,而且实际上它是由内层函数导致的。闭包在栈上“封闭”了局部变量,使其在栈创建执行结束后仍然存在。

def generate_power(number): # define the inner function ... def nth_power(power): return number ** power # ... which is returned by the factory function return nth_power
>>raise_two = generate_power(2)>>print(raise_two(5)) 32

外层函数接受一个参数number=2,然后生成一个nth_power()函数,该函数只接受一个单一的参数power,其中包含number=2

返回的函数被赋值给变量raise_two,我们可以通过raise_two来调用函数并传递变量。

2.装饰器

「装饰器」是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理

装饰器其实就是一个工厂函数,它接受一个函数为参数,然后返回一个新函数,其闭包中包含了原函数

1.简单装饰器

def deco(func): def wrapper(): print "start" func() #调用函数 print "end" return wrapper @deco def myfun(): print "run" myfun()

由于装饰器函数返回的是原函数的闭包wrapper,实际上被装饰后的函数就是wrapper,其运行方式就和wrapper一样。

相当于

myfun=deco(myfun)

2.装饰一个需要传递参数的函数

def deco(func): def wrapper(param): print "start" func(param) print "end" return wrapper @deco def myfun(param): print "run with param %s"%(param) myfun("something")

这种情况下,仍然返回wrapper,但是这个wrapper可以接受一个参数,因此这样的装饰器只能作用于接受一个参数的函数

3.装饰任意参数的函数

def deco(func): def warpper(*args,**kw): print "start" func(*args,**kw) print "end" return warpper @deco def myfun1(param1): print "run with param %s"%(param1) @deco def myfun2(param1,param2): print "run with param %s and %s"%(param1,param2) myfun1("something") myfun2("something","otherthing") # start # run with param something # end # start # run with param something and otherthing # end

两个函数可以被同样一个装饰器所装饰

4.带参数的装饰器

装饰器接受一个函数作为参数,这个毋庸置疑。但是有时候我们需要装饰器接受另外的参数。此时需要再加一层函数,实际上是定义了一个生成装饰器的工厂函数,调用它,搭配需要的参数,来返回合适的装饰器。

def log(text): def deco(func): def wrapper(*args,**kw): print text func(*args,**kw) print text + " again" return wrapper return deco @log("hello") def myfun(message): print message myfun("world") # hello # world # hello again

这里分两步

  • log=log("hello"),把返回的deco函数赋值给log,此时log相当于其包含text=“hello”的闭包
  • myfun=log(myfun),相当于把myfun传入了deco函数,并且返回wrapper,并赋值给myfun,此时myfun相当于其装饰后的闭包。

整体来看是myfun=log("hello")(myfun)

5.

# -*- coding:gbk -*-  '''''示例8: 装饰器带类参数'''  class locker: def __init__(self): print("locker.__init__() should be not called.") @staticmethod def acquire(): print("locker.acquire() called.(这是静态方法)") @staticmethod def release(): print(" locker.release() called.(不需要对象实例)") def deco(cls): '''''cls 必须实现acquire和release静态方法''' def _deco(func): def __deco(): print("before %s called [%s]." % (func.__name__, cls)) cls.acquire() try: return func() finally: cls.release() return __deco return _deco @deco(locker) def myfunc(): print(" myfunc() called.") myfunc() myfunc()

关于wrapper的返回值

上面的代码中,我们的wrapper函数都没有返回值,而是在wrapper中直接调用了func函数,这么做的目的是要在函数运行前后打印一些字符串。而func函数本事也只是打印字符串而已。

但是这么做有时会违背func函数的初衷,比如func函数确实是需要返回值的,那么其装饰后的函数wrapper也应该把值返回。

我们看这样一段函数:

def deco(func): def warpper(*args,**kw): print "start" func(*args,**kw)#直接调用,无返回值 print "end" return warpper @deco def myfun(param): return 2+param sum=myfun(2) #期望纪录返回值并打印 print sum

结果,并没有返回值

>>startendNone

因此我们需要wrapper把函数结果返回:

def deco(func): def warpper(*args,**kw): print "start" result=func(*args,**kw)#纪录结果 print "end" return result #返回 return warpper @deco def myfun(param): return 2**param sum=myfun(2) #这里其实是sum=result print sum

当然,如果不是为了在func前后打印字符串,也可以把func直接返回

关于装饰器装饰过程中函数名称的变化

当装饰器装饰函数并返回wrapper后,原本myfun的__name__就改变了

from time import time,sleepdef timer(func): def warpper(*args,**kw): tic=time() result=func(*args,**kw) toc=time() print func.__name__ print "%f seconds has passed"%(toc-tic) return result return warpper @timer def myfun(): sleep(2) return "end" myfun() print myfun.__name__ #wrapper # myfun # 2.003399 seconds has passed # warpper

这样对于一些依赖函数名的功能就会失效,而且也不太符合逻辑,毕竟wrapper对于我们只是一个中间产物

from time import time,sleepimport functools def timer(func): @functools.wraps(func) def warpper(*args,**kw): tic=time() result=func(*args,**kw) toc=time() print func.__name__ print "%f seconds has passed"%(toc-tic) return result return warpper @timer def myfun(): sleep(2) return "end" myfun() print myfun.__name__ #wrapper # myfun # 2.003737 seconds has passed # myfun

导入模块import functools,并且用@functools.wraps(func)装饰wrapper即可

3.Flask中的@app.route()装饰器

class NotFlask(): def route(self, route_str): def decorator(f): return f return decorator app = NotFlask() @app.route("/") def hello(): return "Hello World!"

route是NotFlask类的一个方法,并且其实际上是一个装饰器工厂,这里我们并没有装饰我们的函数,装饰器仅仅返回了函数的引用而没有装饰它。

class NotFlask(): def __init__(self): self.routes = {} def route(self, route_str): def decorator(f): self.routes[route_str] = f return f return decorator app = NotFlask() @app.route("/") def hello(): return "Hello World!"

现在给装饰器初始化一个字典,在我们传入参数生产装饰器route的时候,把函数存入字典响应位置,key为url字符串,value为相应函数。

不过此时,我们并不能访问这个内部的视图函数,我们需要一个方法来获取相应的视图函数。

class NotFlask(): def __init__(self): self.routes = {} def route(self, route_str): def decorator(f): self.routes[route_str] = f return f return decorator def serve(self, path): view_function = self.routes.get(path)#获取相应函数 if view_function: return view_function()#返回函数 else: raise ValueError('Route "{}"" has not been registered'.format(path)) app = NotFlask() @app.route("/") def hello(): return "Hello World!"

然后我们可以这样,通过url字符串来访问相应的视图函数

app = NotFlask()@app.route("/")def hello(): return "Hello World!" print app.serve("/") #>>Hello World! 博文链接:http://www.jianshu.com/p/1e2394733e77

转载于:https://www.cnblogs.com/zhaoweiblog/p/5899630.html

你可能感兴趣的文章
poj - 1860 Currency Exchange
查看>>
chgrp命令
查看>>
Java集合框架GS Collections具体解释
查看>>
洛谷 P2486 BZOJ 2243 [SDOI2011]染色
查看>>
linux 笔记本的温度提示
查看>>
(转)DOTA新版地图6.78发布:大幅改动 增两位新英雄
查看>>
数值积分中的辛普森方法及其误差估计
查看>>
Web service (一) 原理和项目开发实战
查看>>
跑带宽度多少合适_跑步机选购跑带要多宽,你的身体早就告诉你了
查看>>
广平县北方计算机第一届PS设计大赛
查看>>
深入理解Java的接口和抽象类
查看>>
java与xml
查看>>
Javascript异步数据的同步处理方法
查看>>
快速排序——Java
查看>>
unity游戏与我
查看>>
187. Repeated DNA Sequences
查看>>
iis6 zencart1.39 伪静态规则
查看>>
SQL Server代理(3/12):代理警报和操作员
查看>>
基于事件驱动的DDD领域驱动设计框架分享(附源代码)
查看>>
Linux备份ifcfg-eth0文件导致的网络故障问题
查看>>