装饰器:
- 定义:
- 原则:
- 实现装饰器知识储备:
推导以及铺垫,装饰器是怎么回事
1 #有十个函数,test1,test2都有自己要完成的功能 2 def test1(): 3 pass 4 5 6 def test2(): 7 pass 8 9 #如此调用 10 test1() 11 test2()
函数的代码已完成,But每个方法都要增加记录日志的功能,我们可以怎么做呢?
方法1、一行一行加代码
#乜有学函数之前,一行一行加代码 def test1(): pass print("logging...")#加一行 def test2(): pass print("logging...")#加一行too test1() test2()
方法2、定义一个方法,再调用,若有十个函数就调用十次代码,避免重复的代码。
def logger(): print("logging...") def test1(): pass logger()#这样调用 def test2(): pass logger()#这样调用 test1() test2()
但是如果,你的函数有100多个,且是已经在线上的,肿么办?你好要根据需求变化,新增一个功能。
如果按照方法2,逐个添加调用方法,虽然可以,但是修改了线上源码,修改程序源码,就会有风险发生。导致其他连锁未知反应,就不好了,导致线上的业务可能会崩掉,有风险,不能这么干。写好了程序不能动源码。
2、举例说明装饰器
写一个装饰器代码,体会一下什么叫装饰器
1 import time 2 3 def test1(): 4 time.sleep(3) #逻辑内容 5 print("in the test1") 6 7 8 test1() #调用函数
##########装饰器###########
1 import time 2 #写一个装饰器 3 def timer(func): 4 def warpper(*args,**kwargs): 5 start_time=time.time() 6 func() 7 stop_time=time.time() 8 print("the func run time is %s "%(stop_time-start_time)) 9 return warpper 10 11 @timer #这里调用,写在test1前面 12 def test1(): 13 time.sleep(3) 14 print("in the test1") 15 16 17 test1()
【运行结果】
in the test1
the func run time is 3.000171661376953
情况1、 以下方法,不定义bar(),调用时会报错
1 def foo(): 2 print("in the foo") 3 bar() 4 5 foo()
情况1图解
情况2、bar()的定义在方法foo()的调用之前
1 def bar(): 2 print("in the bar") 3 4 def foo(): 5 print("in the foo") 6 bar() 7 8 9 foo()
【运行结果】
in the foo
in the bar
情况2图解
情况3、bar()的定义在方法foo()的定义之后
1 def foo(): 2 print("in the foo") 3 bar() 4 5 #bar()的定义放在调用函数之前 6 def bar(): 7 print("in the bar") 8 9 foo()
【运行结果】
in the foo
in the bar
情况3图解,同上
情况4、bar()的定义在方法foo()的调用之后
1 def foo(): 2 print("in the foo") 3 bar() 4 5 foo() 6 7 def bar(): 8 print("in the bar")
【运行结果】
Traceback (most recent call last):
in the foo
File "D:/Python/s014/Day4/fuc.py", line 8, in <module>
foo()
File "D:/Python/s014/Day4/fuc.py", line 6, in foo
bar()
NameError: name 'bar' is not defined
3、什么是【函数即“变量”】
变量名,方法名是门牌号,对应到各自的值分配的内存地址上
1 x=1 2 y=x 3 4 def test(): 5 pass
在python解释器中,有一个概念叫做引用基数,那什么叫引用基数,就是比方说,x=1,y=x,它会先在内存当中把1这个值存放下来,这个x,y其实就是"1"的门牌号,也是对"1"的一次引用。python什么时候把这个1这个屋子清空呐?它会等到"1"所对应的门牌号都没有了,就会把"1"这里面的东西给清掉,这个也是python的内存回收机制,就是靠这种方式回收的。
【匿名函数】
#匿名函数 calt=lambda x:x*3 print(calt(3))
lambda x:x*3 是函数体但是没有名字,没有名字就代表能被回收掉,赋值给变量calt,就是给他一个门牌号
4、高阶函数
a、把一个函数名当做实参传给另外一个函数
b、返回值中包含函数名
写一个高阶函数
1 def bar(): 2 print("in the bar") 3 4 def test1(func): 5 print(func) 6 7 test1(bar)
【运行结果】打印了一个内存地址
<function bar at 0x0000000000B57048>
【直接加小括号可以运行吗】
1 def bar(): 2 print("in the bar") 3 4 def test1(func): 5 print(func) 6 func()#这样调用方法可以运行吗 7 8 9 test1(bar) #把 bar函数名当做实参传到test1中 10 11 【运行结果】 12 <function bar at 0x0000000001167048> 13 in the bar
不加小括号,就是一个内存映射的地址,加小括号,就是调用方法,可以运行。
方法一,增加部分功能,统计bar方法的运行时间
1 import time 2 3 def bar(): 4 time.sleep(3) 5 print("in the bar") 6 7 def test1(func): 8 start_time = time.time() 9 func() 10 stop_time = time.time() 11 print("the func run the is %s"%(stop_time-start_time)) 12 13 14 test1(bar) #把 bar函数名当做实参传到test1中,这里改变了调用bar()函数的方法 15
【运行结果】
in the bar
the func run the is 3.0
------------------------------------------------------------------------------
以上代码,test1相当于装饰器,给原来的方法增加了新功能,但是修改了调用方法。
原则应该是【在不修改原源代码的情况下,为其增加新的功能。】 bar()方法没有变
方法二,不修改调用方式的情况下的调用
1 import time 2 3 def bar(): 4 time.sleep(3) 5 print("in the bar") 6 7 def test2(func): 8 print(func) 9 return func #返回函数的内存地址 10 11 12 bar = test2(bar) #把test2(bar)赋给bar方法 13 bar() #没有bar函数改变调用方式
这样赋值,bar=test2(bar)
再调用bar()方法
5、函数嵌套
在一个函数的函数体内,用def 去声明一个函数,而不是去调用其他函数,称为嵌套函数。
函数嵌套的作用:
函数嵌套就是,在一个函数中声名另一个函数,而不是调用它
以下情况是不是嵌套函数呢?
1 #是不是嵌套函数呢 2 def bar(): 3 print("in the bar") 4 def foo(): 5 print("in the foo") 6 bar() #调用bar函数 7 8 foo()
当然不是,以上只适合函数调用而已,在方法内调用bar()函数,而没有声明定义
函数嵌套以及函数的调用
1 def foo(): 2 print("in the foo") 3 def bar():#在foo函数中定义一个函数bar——————函数嵌套 4 print("in the bar") 5 bar()#函数调用 6 foo()
6、函数的作用域
#局部作用域和全局作用域的访问顺序
x=0
def grandpa():-------------1
x=1
def dad():-------------2
x=2
def son():---------3
x=3
print(x)
son()
dad()
#调用grandpa
grandpa()
#输出
3
函数的作用域,从外往里,逐层查找
如果只声明,不调用,定义达到dad(),son()会是什么样的执行结果呢?
x=0 def grandpa(): x=1 def dad(): x=2 def son(): x=3 print(x) grandpa()
不调用相当于声明了一个变量 ,什么都没有做,不会有值产生dad(),son()
4-1.2、装饰器定义
1、首先装饰器实现的条件:高阶函数+嵌套函数 -->装饰器
1 import time 2 3 def timmer(func):---------------高阶函数 4 def deco():-----------------嵌套函数 5 start_time = time.time() 6 func() #run test1() 7 stop_time = time.time() 8 print("the func run time is %s"%(stop_time-start_time)) 9 return deco 10 11 @timmer # 相当于test1 = timmer(test1) 12 def test1(): 13 time.sleep(3) 14 print("in the test1") 15 16 test1()
【输出结果】
in the test1
the func run time is 3.0002999305725098
没有改变调用方式,没有修改函数源码
执行顺序:
step1、def timmer(func)直接跳过,因为只创建没有执行
step2、@timmer,这是一步运行的操作。所以又回到timmer(func),相当于赋值操作test1 = timmer(test1),逐步执行timmer函数中的每一步,然后return.
step3、走到test1()函数调用处。然后,跳到def deco():执行里面的代码,此处说明test1()已经被换成了deco()的意思。
step4、func()此步执行test1()
2、如果函数带参数的情况,执行结果是怎样的呢?
1 import time 2 3 def timmer(func): #timmer(test1) func=test1 4 def deco(): 5 start_time = time.time() 6 func() #run test1() 7 stop_time = time.time() 8 print("the func run time is %s"%(stop_time-start_time)) 9 return deco 10 11 @timmer 12 def test2(name,age): 13 print("name:%s,age:%s"%(name,age)) 14 15 test2()
【运行结果——报错】
C:UsersAdministratorAppDataLocalProgramsPythonPython35python.exe D:/PythonStudy3.5/Code1/Day4/dic.py Traceback (most recent call last): File "D:/PythonStudy3.5/Code1/Day4/dic.py", line 15, in <module> test2() File "D:/PythonStudy3.5/Code1/Day4/dic.py", line 6, in deco func() #run test1() TypeError: test2() missing 2 required positional arguments: 'name' and 'age'
Process finished with exit code 1
报错的原因:
test2函数其实就是执行的deco函数,deco函数体内的func()其实就是执行test2函数,但是,test2需要传入name和age两个参数,所以报错。
1 import time 2 def timmer(func): #timmer(test1) func=test1 3 def deco(*args,**kwargs): #传入非固定参数 4 start_time = time.time() 5 func(*args,**kwargs) #传入非固定参数 6 stop_time = time.time() 7 print("the func run time is %s"%(stop_time-start_time)) 8 return deco 9 10 #不带参数 11 @timmer # 相当于test1 = timmer(test1) 12 def test1(): 13 time.sleep(3) 14 print("in the test1") 15 16 #带参数 17 @timmer 18 def test2(name,age): 19 print("name:%s,age:%s"%(name,age)) 20 #调用 21 test1() 22 test2("Cola",3)
【运行结果】
in the test1
the func run time is 3.0
name:Cola,age:3
the func run time is 0.0
def deco(*args,**kwargs): #传入非固定参数
不能确定传入几个参数,所以我们只能用非固定参数传参。
3、执行函数有返回值
1 import time 2 3 def timmer(func): #timmer(test1) func=test 4 def deco(*args,**kwargs): 5 print("定义一个 res 接收返回值") 6 res = func(*args,**kwargs) #这边传入函数结果赋给res 7 print("res 接收完毕,要return喽") 8 return res # 返回res 9 return deco 10 11 12 @timmer 13 def test1(): # test1 = timmer(test1) 14 print("in the test1") 15 return "from the test1" #执行函数test1有返回值 16 17 18 19 res = test1() 20 print("test1() run over ...") 21 print("res 是什么呢 ...") 22 print(res)
【运行结果】
定义一个 res 接收返回值
in the test1
res 接收完毕,要return喽
test1() run over ...
res 是什么呢 ...
from the test1
其实就是在内置函数中把传入参数的执行结果赋给res,然后再返回res变量。
4、带参数的装饰器
在你访问不通页面时,你用的验证的方式来源不同,这时你该怎么办?
step1给方法增加装饰器名字
step2 定义装饰器实现的方法
step3 调用
step4、给调用方法增加返回值
step5 调用方式的区分
step6、增加一层,传进来的参数,打印参数,调用方法
step7、增加判断条件
1 #本地验证 2 user,passwd = "user1","pwd123" 3 def auth(auth_type): #传递装饰器的参数 4 print("auth func:",auth_type) 5 def outer_wrapper(func): # 将被装饰的函数作为参数传递进来 6 def wrapper(*args,**kwargs): #将被装饰函数的参数传递进来 7 print("wrapper func args:",*args,**kwargs) 8 username = input("Username:").strip() 9 password = input("Password:").strip() 10 if auth_type == "local": 11 if user == username and passwd == password: 12 print("