day04-装饰器
Posted 涅槃之路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了day04-装饰器相关的知识,希望对你有一定的参考价值。
一、装饰器定义
1)装饰器:本质是函数。
2)功能:用来装饰其他函数,顾名思义就是,为其他的函数添加附件功能的。
二、原则
1)不能修改被装饰函数的源代码
2)不能修改被装饰函数的调用方式
如果你写的这个程序在生产环境下已经运行了,如果修改别人的源代码或者修改别人的调用方式,那么出了问题,后果可想而知
三、实现装饰器知识储备
1)函数即"变量"
2)高阶函数
3)嵌套函数
最终: 高阶函数+嵌套函数 => 装饰器
3.1 函数即变量
1)python的内存机制
#变量
x = 1
#函数
def test():
pass
以上一个变量一个函数在内存中的表现形式如下图:
在python解释器中,有一个概念叫做引用基数,比方说,x=1,它会先在内存当中把1这个值实实在在的存放下来,这个x其实就是1的门牌号,也是对1的一次引用。python什么时候把这个1这个屋子清空呐?它会等到1所对应的门牌号都没有了,就会把1这里面的东西给清掉,这个也是python的内存回收机制,就是靠这种方式回收的。
2)del清理
那我们用什么清理呐?用del去清理门牌号,就是对1的值引用的变量,del x就表示清理掉1对应的x的门派号。如果x没有被del,则x永远不还被删除,除非程序结束了,不然永远不会被删除。del删除的不是1,只是把门牌号x删除了,只是定期刷新时,发现1没有被其他门牌号引用了,才会被清掉。
3)函数在内存的表现形式
①bar函数在foo函数之后定义
#bar函数在foo函数之后定义 def foo(): print("in the foo") bar() def bar(): print("in the bar") foo()
#输出
in the foo
in the bar
②bar函数是在foo函数之前定义
# bar函数是在foo函数之前定义 def bar(): print("in the bar") def foo(): print("in the foo") bar() foo()
#输出
in the foo
in the bar
显然,两种写法效果是一样的,那我们来看看第三种情况。
③bar函数在foo函数调用之后声明
# bar函数在foo函数调用之后声明 def foo(): print("in the foo") bar() foo() def bar(): print("in the bar")
#输出
Traceback (most recent call last):
in the foo
File "D:/PycharmProjects/pyhomework/day4/装饰器/函数即变量.py", line 31, in <module>
foo()
File "D:/PycharmProjects/pyhomework/day4/装饰器/函数即变量.py", line 29, in foo
bar()
NameError: name ‘bar‘ is not defined #bar函数没有定义
3.2 高阶函数
实现高阶函数有两个条件:
1)把一个函数名当做实参传给另外一个函数
2)返回值中包含函数名
1、把一个函数名当做实参传给另外一个函数
作用:在不修改被装饰函数源代码的情况下为其添加功能
import time def bar(): time.sleep(3) print("in the bar") def test1(func): print(func) #相当于print(bar) 函数的内存地址 start_time = time.time() func() #相当于bar() 进入函数内部执行 stop_time = time.time() print("the func run the is %s"%(stop_time-start_time)) #没有修改bar的代码 test1(bar) #把bar函数名当做实参传到test1中
#输出
<function bar at 0x0000000000A7D378> #bar函数的内存地址
in the bar #函数值
the func run the is 2.9912972450256348
2、返回值中包括函数名
作用:不修改函数调用方式
import time def bar(): time.sleep(3) print("in the bar") def test2(func): print(func) return func #返回函数的内存地址 #调用test2函数 bar = test2(bar) #重新给bar赋值,打印内存地址(内存地址加上小括号就能打印函数值) bar() #bar函数调用方式不变,实现装饰器的功能
相当于@bar
#输出
<function bar at 0x0000000000B6D378> #打印bar函数的内存地址
in the bar
3.3 嵌套函数
1、定义
在一个函数的函数体内,用def 去声明一个函数,而不是去调用其他函数,称为嵌套函数。
嵌套函数例子:
def foo(): print("in the foo") def bar(): #在foo函数体内,用def声明一个函数 print("in the bar") bar() #调用foo函数 foo()
#输出
in the foo
in the bar
下面这种情况是不是嵌套函数?
def bar(): print("in the bar") def foo(): print("in the foo") bar() #调用bar函数 foo()
很显然不是,因为只是调用了bar函数,没有用def去声明一个函数。
局部作用域和全局作用域的访问顺序
#局部作用域和全局作用域的访问顺序 x=0 def grandpa(): x=1 def dad(): x=2 def son(): x=3 print(x) son() dad() #调用grandpa grandpa()
很显然最后输出的是3,这个说明作用域:只能是从里往外找,一层一层的的找。
四、装饰器实现
4.1 定义
装饰器实现的条件:高阶函数+嵌套函数 =》装饰器
import time #定义内置函数 def timmer(func): #timmer(test1) func=test1 def deco(): start_time = time.time() func() #run test1() stop_time = time.time() print("the func run time is %s"%(stop_time-start_time)) return deco #返回deco的内存地址 #装饰test1函数 @timmer # 相当于test1 = timmer(test1) test1(),调用deco的内存值。同时也有定义一个test1的变量 def test1(): time.sleep(3) print("in the test1") #直接执行test1函数 test1()
#输出
in the test1
the func run time is 3.0002999305725098
执行步骤:
- 执行timmer函数,timmer(test1) 返回值赋值给test1变量,即test1=timmer(test1)
- 此时的test1的值是执行timmer函数返回值deco,即test1=deco
- 所以执行test1,其实就是执行的是deco函数,test1()其实就是执行deco函数。
4.2 执行函数带参数
import time def timmer(func): #timmer(test2) func=test2 def deco(): start_time = time.time() func() #run test2() stop_time = time.time() print("the func run time is %s"%(stop_time-start_time)) return deco @timmer def test2(name,age): print("name:%s,age:%s"%(name,age)) test2()
#输出
Traceback (most recent call last):
File "D:/PycharmProjects/pyhomework/day4/装饰器/装饰器高潮.py", line 23, in <module>
test2()
File "D:/PycharmProjects/pyhomework/day4/装饰器/装饰器高潮.py", line 8, in deco
func() #run test1()
TypeError: test2() missing 2 required positional arguments: ‘name‘ and ‘age‘ #缺少传入name和age参数
很显然是错误的。因为这边执行的test2函数其实就是执行的deco函数,deco函数体内的func()其实就是执行test2函数,但是,test2需要传入name和age两个参数,所以报错。那怎么解决呢?
传入确定参数:
import time def timmer(func): #timmer(test1) func=test1 def deco(name,age): start_time = time.time() func(name,age) #run test2() stop_time = time.time() print("the func run time is %s"%(stop_time-start_time)) return deco @timmer def test2(name,age): print("name:%s,age:%s"%(name,age)) test2(‘zhou‘,22)
不能确定传入几个参数,所以我们用非固定参数传参。代码如下:
import time def timmer(func): #timmer(test1) func=test1 def deco(*args,**kwargs): #传入非固定参数 start_time = time.time() func(*args,**kwargs) #传入非固定参数 stop_time = time.time() print("the func run time is %s"%(stop_time-start_time)) return deco #不带参数 @timmer # 相当于test1 = timmer(test1) def test1(): time.sleep(3) print("in the test1") #带参数 @timmer def test2(name,age): print("name:%s,age:%s"%(name,age)) #调用 test1() test2("Alex",22)
#输出
#test1
in the test1
the func run time is 3.0010883808135986
#test2
name:Alex,age:22
the func run time is 0.0 #test2
4.3 执行函数有返回值
def timmer(func): #timmer(test1) func=test1 def deco(*args,**kwargs): res = func(*args,**kwargs) #这边传入函数结果赋给res return res # 返回res return deco @timmer def test1(): # test1 = timmer(test1) print("in the test1") return "from the test1" #执行函数test1有返回值 res = test1() print(res)
#输出
in the test1
from the test1
通过上面的例子,可以看出,其实就是在内置函数中把传入参数的执行结果赋给res,然后再返回res变量。
4.4带参数装饰器
之前我们的装饰器都是没有带参数的,其实我们已经能解决90%的问题了,但是如果说有一种情况:就是在你访问不通页面时,你用的验证的方式来源不同,这时你该怎么办?
#本地验证 user,passwd = "zhouqiongjie","abc123" def auth(auth_type): #传递装饰器的参数 print("auth func:",auth_type) def outer_wrapper(func): # 将被装饰的函数作为参数传递进来 def wrapper(*args,**kwargs): #将被装饰函数的参数传递进来 print("wrapper func args:",*args,**kwargs) username = input("Username:").strip() password = input("Password:").strip() if auth_type == "local": if user == username and passwd == password: print("\033[32mUser has passed authentication\033[0m") res = func(*args,**kwargs) print("--after authentication") return res else: exit("Invalid username or password") elif auth_type == "ldap": pass return wrapper return outer_wrapper def index(): print("welcome to index page") @auth(auth_type="local") #带参数装饰器 def home(): print("welcome to home page") return "from home" @auth(auth_type="ldap") #带参数装饰器 def bbs(): print("welcome to bbs page") index() print(home()) bbs()
上面的例子可以看出,执行步骤:
1) outer_wrapper = auth(auth_type="local")
2) home = outer_wrapper(home)
3)home()
所以这个函数的作用分别是:
1) auth(auth_type) 传递装饰器的参数
2) outer_wrapper(func) 把函数当做实参传递进来
3)wrapper(*args,**kwargs) 真正执行装饰的函数
以上是关于day04-装饰器的主要内容,如果未能解决你的问题,请参考以下文章