说到装饰器是我们每个学Python人中的心痛。
1.闭包
学习装饰器首先我们先学习闭包:
闭包条件
1 在一个外函数中定义了一个内函数。
2 内函数里使用了外函数的临时变量。
3 并且外函数的返回值是内函数的引用(即函数名)。
闭包的定义
闭包的概念就是当我们在函数内定义一个函数时,这个内部函数使用了外部函数的临时变量,且外部函数的返回值是内部函数的引用时,我们称之为闭包。
出现的背景
一般情况下,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
闭包的基本结构:
def func1(msg): #外函数 name="xiao" def inner(): #内函数 na = name # 使用外函数的变量 nb = msg print(inner.__closure__) print(inner.__closure__[0].cell_contents) print(inner.__closure__[1].cell_contents) return inner #返回内函数的引用 f=func1(123) f() #判断函数是不是闭包函数用__closure__方法 如果是闭包函数就是返回一个元组包含<cell, # 如果不是闭包函数就就返回none。这个cell包含了我们被引用的所有外部变量
结果:
(<cell at 0x000001EFD0610CA8: int object at 0x00000000699F7D20>, <cell at 0x000001EFD0610CD8: str object at 0x000001EFD04B4C38>) 123 xiao
闭包存在的意义在哪里?
我认为闭包存在的意义在于它保存了外层函数的变量,如果他不能保存外存函数的变量,那么闭包和其他函数没有什么区别。
例子:
def get_avg(): scores = [] def inner_count_avg(val): scores.append(val) return sum(scores)/len(scores) return inner_count_avg avg = get_avg() print(avg(5)) # 相当于 inner_count_avg(5) print(avg(6)) # inner_count_avg(6) # 这样看来外函数get_avg只被执行了一次
结果:
5.0 5.5
2.装饰器
装饰器:装饰器是一个函数,这个函数是用来装饰其他函数的,作用:在不改变原函数的调用方式和内部代码为其他函数添加新功能。
装饰器的内层函数就是一个闭包,因为它使用了外部函数的参数func,
原则:1.不能改变被修饰函数的源代码。
2.不能修改被修饰函数的调用方式。
高阶函数
高阶函数:一个函数接收另一个函数名当做参数,或者把另一个函数作为结果返回的函数就是高阶函数.这是<<流畅的Python>>一书中定义的. 装饰器也是高阶函数.
补充:
嵌套函数:在一个函数里定义新的一个函数。
装饰器=高阶函数+嵌套函数
装饰器的两大特性
特性一:能把被装饰的函数替换成其他函数.
特性二:装饰器在加载模块的时候立即执行.
registry = [] def register(func): print("runnning register(%s)" % func) registry.append(func) return func @register def f1(): print('runing f1()') @register def f2(): print('runing f2()') def f3(): print('runing f3()') def main(): print('running main()') print('registery->', registry) f1() f2() f3() if __name__ == '__main__': main()
在其他py里导入该模块时
runnning register(<function f1 at 0x10ccba840>)
runnning register(<function f2 at 0x10ccba8c8>)
上边的例子说明了加载该模块时,装饰器就已经运行,被装饰器装饰的函数只有在明确的调用的时候才运行.
装饰器例子
例子要求:1.写一个装饰器,计算两个程序运行耗时。
2.不改变源代码和调用方式。
例子:
1 # Author :ZHANG 2 #-*-coding:utf-8-*- 3 import time 4 def text1(): 5 time.sleep(3) 6 print("in the text1") 7 def text2(): 8 time.sleep(2) 9 print("in the text2")
方法:
1 # Author :ZHANG 2 #-*-coding:utf-8-*- 3 import time 4 def solu(func): 5 def wreppe(): 6 star_time=time.time() 7 func() 8 stop_time=time.time() 9 print("耗时为%s秒."%(stop_time-star_time)) 10 return wreppe 11 @solu #这里其实为:text1=solu(text1) 12 def text1(): 13 time.sleep(3) 14 print("in the text1") 15 @solu #同text1 16 def text2(): 17 time.sleep(2) 18 print("in the text2") 19 text1() #这里并不是真的text1(),其实是一个替代,solu(text1)() 20 text2() #同text1
结果:
1 in the text1 2 耗时为3.000171661376953秒. 3 in the text2 4 耗时为2.0001144409179688秒.
这里我们要特别注意这个@符号,其实可以用下面的代码来代替只不过我们用@比较简便罢了,但是我们要知道真正的意义
import time def solu(func): def wreppe(): star_time=time.time() func() stop_time=time.time() print("耗时为%s秒."%(stop_time-star_time)) return wreppe def text1(): time.sleep(3) print("in the text1") text1=solu(text1)#这里其实就是变量值替换了 text1() #这里并不是真的text1(),其实是一个替代,solu(text1)()
继续增加要求:text2加上多个参数,包括关键参数
方法:
1 # Author :ZHANG 2 #-*-coding:utf-8-*- 3 import time 4 def solu(func): 5 def wreppe(*args,**kwargs): 6 star_time=time.time() 7 func(*args,**kwargs) 8 stop_time=time.time() 9 print("耗时为%s秒."%(stop_time-star_time)) 10 return wreppe 11 @solu #这里其实为:text1=solu(text1)=wreppe 12 def text1(): 13 time.sleep(3) 14 print("in the text1") 15 @solu #同text1 16 def text2(*args,**kwargs): 17 time.sleep(2) 18 print("text2:",args,kwargs) 19 text1() #这里并不是真的text1(),其实是一个替代,solu(text1)() 20 text2(2,"we",a=1) #同text1
结果:
1 in the text1 2 耗时为3.000171661376953秒. 3 text2: (2, 'we') {'a': 1} 4 耗时为2.0001144409179688秒.
马上到高潮:
新例子要求为:
home函数return后面添加“from home”
1 #-*-coding:utf-8-*- 2 username,password="zhang",12345 3 def deco(func): 4 def wreppe(*args,**kwargs): 5 user=input("enter your name>>>").strip() 6 passwd=int(input("enter your password").strip())#密码输入这里我简化了许多,现在是为了突出另外一个主题 7 if user==username and passwd==password: 8 func(*args,**kwargs) 9 print("