1 ------装饰器 2 """首先在Python中的函数是可以看做一个特殊变量的.而装饰器是建立 3 在闭包的前提上的. 4 闭包就是将函数当做参数传入另一个函数,两个函数的嵌套,外部函数返回 5 内部函数的引用 6 .装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何 7 代码变动的前提下增 8 加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需 9 求的场景, 10 比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰 11 器是解决这类问 12 题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无 13 关的雷同代码并继续 14 重用。 15 装饰器原则: 16 17 不改变源代码 - 因为函数可能在其他地方各种调用,一改动全身 18 19 -- 不改变原函数调用顺序 - 源代码有自己的逻辑处理 20 装饰器又叫做语法糖 21 22 闭包简介------------------------------ 23 如果在一个函数的内部定义了另一个函数,外部的我们叫他外函数, 24 内部的我们叫他内函数。 25 闭包: 26 在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量, 27 并且外函数的 28 返回值是内函数的引用。这样就构成了一个闭包。 29 """ 30 #闭包函数的实例 31 2 # outer是外部函数 a和b都是外函数的临时变量 32 3 def outer( a ): 33 4 b = 10 34 5 # inner是内函数 35 6 def inner(): 36 7 #在内函数中 用到了外函数的临时变量 37 8 print(a+b) 38 9 # 外函数的返回值是内函数的引用 39 10 return inner 40 11 41 12 if __name__ == '__main__': 42 13 # 在这里我们调用外函数传入参数5 43 14 #此时外函数两个临时变量 a是5 b是10 ,并创建了内函数,然后把内函数的引用返回存给了demo 44 15 # 外函数结束的时候发现内部函数将会用到自己的临时变量,这两个临时变量就不会释放,会绑定给这个内部函数 45 16 demo = outer(5) 46 17 # 我们调用内部函数,看一看内部函数是不是能使用外部函数的临时变量 47 18 # demo存了外函数的返回值,也就是inner函数的引用,这里相当于执行inner函数 48 19 demo() # 15 49 50 在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间 51 的变量,需要向上一层变量空间找这个变量。内函数修改闭包变量的方法通过nonlocal关键字。 52 1 #修改闭包变量的实例 53 2 # outer是外部函数 a和b都是外函数的临时变量 54 3 def outer( a ): 55 4 b = 10 # a和b都是闭包变量 56 5 c = [a] #这里对应修改闭包变量的方法2 57 6 # inner是内函数 58 7 def inner(): 59 8 #内函数中想修改闭包变量 60 9 # 方法1 nonlocal关键字声明 61 10 nonlocal b 62 11 b+=1 63 12 # 方法二,把闭包变量修改成可变数据类型 比如列表 64 13 c[0] += 1 65 14 print(c[0]) 66 15 print(b) 67 16 # 外函数的返回值是内函数的引用 68 17 return inner 69 18 70 19 if __name__ == '__main__': 71 20 72 21 demo = outer(5) 73 22 demo() # 6 11 74 还有一点需要注意:使用闭包的过程中,一旦外函数被调用一次返回了内函数的引用,虽然每次调用内函数,是开启一个函数执行过后消亡,但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量 75 76 上代码! 77 78 def outer(x): 79 def inner(y): 80 nonlocal x 81 print('x', x) 82 x+=y 83 84 print('y',y) 85 return x 86 return inner 87 a=outer(10) 88 print(a(1)) 89 print(a(3)) 90 91 # x 10 92 # y 1 93 # 11 94 # x 11 95 # y 3 96 # 14 97 98 两次分别打印出11和14,由此可见,每次调用inner的时候,使用的闭包变量x实际上是同一个。 99 100 101 #------------------------------------装饰器 102 import time 103 def test(): 104 time.sleep(2) 105 print("test is runing") 106 107 def jq(func): 108 start=time.time() 109 print('start',start) 110 func() 111 end=time.time() 112 print('end',end) 113 print(end-start) 114 jq(test) 115 116 # start 1552095544.027457 117 # # test is runing 118 # # end 1552095546.028318 119 # # 2.0008609294891357 120 121 122 import time 123 def test(): 124 time.sleep(2) 125 print("test is runing") 126 127 def jq(func): 128 start=time.time() 129 func() 130 end=time.time() 131 print(end-start) 132 return func 133 test=jq(test) 134 test() 135 136 # test is runing 137 # 2.0007572174072266 138 # test is runing 139 140 import time 141 def jq(func): # 这里的参数就是test 142 def wrapper(): 143 print(456) 144 start=time.time() 145 func() #现在去执行函数test,执行完test,接着往下执行 146 end=time.time() 147 print(end-start) 148 print('执行结束') 149 return wrapper # 执行函数wrapper 150 @jq #在执行函数test之前先执行函数jq 151 def test(): 152 print(123) 153 time.sleep(2) 154 print("test is runing") 155 test() 156 157 # 456 158 # 123 159 # test is runing 160 # 2.0007762908935547 161 # 执行结束 162 163 164 165 166 # @myzsq #添加装饰器 167 # def test(a,b): 168 # print("are you ok") 169 # return a*b 170 # 171 # print(test(4,8)) 172 173 一、无参装饰器 174 def myzsq(func):#func为装饰器绑定的方法 175 def tt(): 176 print("开始") 177 cc=func()#调用test函数 178 print("结束") 179 return tt 180 @myzsq #添加装饰器 181 def test(): 182 print("are you ok") 183 test() 184 185 # 开始 186 # are you ok 187 # 结束 188 189 二、带参装饰器 190 def myzsq(func): 191 def tt(x,y):#传入test的方法参数 192 print("开始") 193 cc=func(x,y) 194 print("结束") 195 return cc 196 return tt 197 @myzsq 198 def test(a,b): 199 print("are you ok",a,b) 200 return a+b+1 201 202 print(test(3,4)) 203 204 # 开始 205 # are you ok 3 4 206 # 结束 207 # 8 208 209 (3)装饰器即可装饰带参函数也可以装饰不带参函数 210 211 def myzsq(func): 212 def tt(*args,**kwargs):#传入test的方法参数 213 print("开始") 214 cc=func(*args,**kwargs) 215 print("结束") 216 return cc 217 return tt 218 219 @myzsq #添加装饰器 220 def test(a): 221 print("are you ok",a) 222 return 33 223 224 print(test(10)) 225 226 # 开始 227 # are you ok 10 228 # 结束 229 # 33 230 231 232 """ 233 1、如果想让装饰器不修改被装饰函数的返回值,我们需要在装饰器中接受被装饰函数的返回值 234 并Return即可。 235 236 (2) 如果希望对被装饰函数进行分类处理,我们可以在绑定装饰器时传入一个参数用于 237 对被装饰函数进行分类,但是这样我们需要在装饰器中在套一层函数,在第一层接收装 238 饰器传递的参数,在第二层函数中接收被装饰函数。 239 240 (3) 如果希望装饰器既能装饰带参的函数也可以修饰不带参的函数,我们只需要在装饰 241 器中接收参数时,把参数定义为非固定参数即可。 242 243 """ 244 (4)装饰器的高级使用 245 246 以上的装饰器已经可以满足基本需求了,但是他们第一存在一个问题,被装饰的函数的返回结果会被修改。 247 248 这里我们模拟一个登陆装饰器,装饰器需要对不同的被装饰函数使用不同的登陆方法,并且是返回值不被修改。 249 250 user, password = 'db', '12345' 251 252 # 当装饰器也需要参入参数时我们需要给装饰器再加一层函数, 253 # 此时装饰器接受到的方法需要进入第二层函数进行接受, 254 # 第一层需要接受装饰器自己的参数 255 256 def login(login_type): 257 def outer_wrapper(func): 258 def wrapper(*agr1, **kwargs): 259 usernameInput = input("UserName:").strip() 260 passwordInput = input("Password:").strip() 261 if login_type == "local": 262 if user == usernameInput and password == passwordInput: 263 print("login successful") 264 res = func(*agr1, **kwargs) # 接受返回结果 265 return res 266 else: 267 print("login fail") 268 elif login_type == "ldap": 269 print("远程登录") 270 return wrapper 271 return outer_wrapper 272 273 274 def index(): 275 print("welcome to index page") 276 277 278 @login(login_type="local") # 对装饰分类 279 def home(): 280 print("welcome to home page") 281 return "from home" 282 283 284 @login(login_type="ldap") # 对装饰分类 285 def blog(): 286 print("welcome to blog page") 287 288 289 index() 290 print(home()) 291 blog() 292 293 294 test = timer(test) 295 显然有些麻烦,Python提供了一种语法糖,即: 296 @timer 297 这两句是等价的,只要在函数前加上这句,就可以实现装饰作用。 298 299 #--------------例如改变装饰函数的返回结果 300 def myzsq(func): 301 def tt(*arg,**kwargs):#传入test的方法参数 302 print("开始") 303 cc=func(*arg,**kwargs) 304 print(arg) #(4,8) 305 print("结束") 306 return arg[0]+arg[1] #将相乘的返回结果改为相加的返回结果 307 return tt 308 @myzsq #添加装饰器 309 def test(a,b): 310 print("are you ok") 311 return a*b 312 313 print(test(4,8))