day4-装饰器前奏
定义
装饰器:本质是函数
功能:装饰其他函数,就是为其他函数添加附加功能
原则
1.不能修改被装饰的函数的源代码
2.不能修改被装饰函数的调用方式
def logging(): print("logging...") #正确写法,没有修改源码 def test1(): pass #错误写法,不能修改源码 def test1(): pass logging() # 调用方式,也不能被修改 test1()
实现装饰器的知识储备
1.函数即"变量"
2.高阶函数
3.嵌套函数
高阶函数+嵌套函数=》装饰器
1.函数即"变量
(1).python的内存机制
#变量 x = 1 #函数 def test(): pass
(2).del清理
那我们用什么清理呐?用del去清理门牌号,就是对1的值引用的变量,del x就表示清理掉1对应的x的门派号。如果x没有被del,则x永远不还被删除,除非程序结束了,不然永远不会被删除。del删除的不是1,只是把门牌号x删除了,只是定期刷新时,发现1没有被其他门牌号引用了,才会被清掉。
(3).函数在内存的表现形式
我们先通过四个例子来解释一下:
1) bar函数在foo函数之前定义
def bar(): print("in the bar") def foo(): print("in the foo") bar() foo() #输出 in the foo in the bar
2) bar函数在foo函数之后定义
def foo(): print("in the foo") bar() def bar(): print("in the bar") foo() #输出 in the foo in the bar # 函数即"变量",bar()有对应的房间(函数体),python执行是按照解释器一行行解释,将函数体放入内存,只要函数体在内存中存在,才可以进行调用
3) bar函数在foo函数调用之后声明
def foo(): print("in the foo") bar() foo() def bar(): print("in the bar") #输出 in the foo NameError: name 'bar' is not defined # 函数即"变量",在foo()执行前,bar()没有对应的房间(函数体),所以不能被调用
2.高阶函数
实现高阶函数的两个必要条件:
a.把一个函数名当做实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能)
b.返回值中包含函数名(不修改函数的调用方式)
a.把一个函数名当做实参传给另外一个函数
作用:在不修改被装饰函数源代码的情况下为其添加功能
def bar(): print("in the bar") def test1(func): #首先定义了一个bar的函数 print(func) #相当于print(bar) func() #相当于bar() test1(bar) #bar(门牌号)就是这个函数映射的内存地址 #输出 <function bar at 0x10197a950> in the bar
先看下面例子:
def bar(): pass print(bar) bar() #bar()可以运行此函数 #输出 <function bar at 0x10217a950>
那么我们为什么要把bar传到test1里面去执行呢?直接bar()运行就好了啊:
import time def bar(): time.sleep(3) print("in the bar") def test1(func): # test1(bar)中的bar传给了func,相当于func = bar,那么func()可以运行 start_time=time.time() #计时,接着运行func()变为了bar(),同时运行bar函数,打印 func() #run bar() stop_time=time.time() #计时 print("the func run time %s"%(stop_time-start_time)) test1(bar) # 首先执行test1(),在test1中将bar传递给了func:相当于把bar的内存地址传给了变量func,回到我们所说的函数即变量,如x=1,y=x y代表x,类似于bar是函数,意味着它也可以像变量一样进行赋值。 #输出 in the bar the func run time 3.0033440589904785 #打印bar函数运行的时间
查看test1(bar)符合装饰器的原则吗?
1.没有修改源代码bar()
2.但是调用的方式变了,不是直接bar()运行,不符合原则,此种test(bar)的调用方式,在函数多次调用的情况下,由于调用次数太多,可能会不知道在哪里调用了bar的函数。
b.返回值中包含函数名
作用:不修改函数的调用方式
import time def bar(): time.sleep(3) print("in the bar") def test2(func): print(func) return func print(test2(bar)) #输出 <function bar at 0x10217a950> <function bar at 0x10217a950>
有内存地址说明加上()就可以直接运行,那么
import time def bar(): time.sleep(3) print("in the bar") def test2(func): print(func) return func print(test2(bar)) test2(bar()) #首先执行test2(),在test2中将bar()传递给了func:相当于把bar()的返回值传给了变量func,那么就不符合高阶函数的定义 #输出 in the bar None
所以我们需要改变运行方式:
t=test2(bar) t() #输出 <function bar at 0x10297a950> in the bar
再改变——>>>符合高阶函数的定义:返回值中包含函数名(不修改函数的调用方式)
import time def bar(): time.sleep(3) print("in the bar") def test2(func): print(func) return func bar=test2(bar) bar() #输出 <function bar at 0x10217a950> in the bar
3.嵌套函数
定义:在一个函数的函数体内,用def去声明一个函数def,而不是去调用它()
def foo(): print("in the foo") def bar(): print("in the bar") bar() #无法调用,def bar()身处函数中,我们所说的函数即变量,那么它存在于局部作用域,局部变量是不能被全部变量调用的
所以我们需要修正到:
def foo(): print("in the foo") def bar(): #全局变量和局部变量,全局变量顶格写,在整个程序运行都存在,局部变量存在于过程或函数中,函数运行完就释放 print("in the bar") bar() foo()
高阶函数+嵌套函数=》装饰器
局部作用域和全局作用域的访问顺序:
x=0 def grandpa(): x=1 def dad(): x=2 def son(): x=3 print(x) son() dad() grandpa() #定义函数就是在定义变量,那么定义完变量后,需要进行调用