一、定义
1、装饰器:本质是函数。
2、功能:用来装饰其他函数,顾名思义就是,为其他的函数添加附件功能的。
3、原则:不能修改被装饰函数的源代码、不能修改被装饰函数的调用方式
4、函数即"变量", 高阶函数+嵌套函数 => 装饰器
def test(): print("LOVE") #正确写法,没有修改源码 def test1(): print("LOVE ME")
#错误写法,不能修改源码 def test1(): print("LOVE ME") test() #调用 test1()
二、函数即变量
1、python的内存机制
在python解释器中,有一个概念叫做引用基数,例如,x=1这个b变量,先在内存当中把1这个值存放下来,这个x其实就是1的门牌号,x=1就是对1的一次引用。
python等到1所对应的门牌号都没有了,就会把1给清掉,这个也是python的内存回收机制。
python用del去清理门牌号,就是对1的值引用的变量,del x就表示清理掉1对应的x的门牌号。如果x没有被del,则x永远不会被删除,除非程序结束了。del删除的不是1,只是把门牌号x删除了,定期刷新时,发现1没有被其他门牌号引用了,1才会被清掉。
#变量 x = 1 #函数 def test(): pass
2、函数在内存的表现形式
①test函数在fex函数之后定义
def test(): print("in test") fex() def fex(): print("in fex") test() #结果 in test in fex
②test函数在fex函数之前定义
def fex(): print("in fex") def test(): print("in test") fex() test()
#结果 in test in fex
上面两种写法,输出结果是一样的
③test函数在fex函数之后声明
def test():
print("in test")
fex()
test()
def fex():
print("in fex")
#结果 Traceback (most recent call last): File "/Users/bianbian/PycharmProjects/test/test10.py", line 5, in <module> test() NameError: name 'test' is not defined
因为在执行test函数时,当调用fex函数时,fex还函数还定义,所以报错。
三、高阶函数
实现高阶函数的条件:把一个函数名当做实参传给另外一个函数,且返回值中包含函数名
1、把一个函数名当做实参传给另外一个函数
import time def fex(): time.sleep(3) print("in the fex") def test(func): print(func) start_time = time.time() func() stop_time = time.time() print("the func run the is %s" % (stop_time - start_time)) #没有修改fex的代码 test(fex) #把fex函数名当做实参传到test中 #结果 <function fex at 0x10df3b268> in the fex the func run the is 3.0029611587524414
作用:在不修改被装饰函数源代码的情况下为其添加功能
2、返回值中包括函数名
import time def fex(): time.sleep(3) print("in the fex") def test1(func): print(func) return(func) #返回函数的内存地址 test=test1(fex) fex() #没有改变fex函数的调用方式 #结果 <function fex at 0x1090ab268> in the fex
作用:不修改函数调用方式
四、嵌套函数
在一个函数的函数体内,用def 去声明一个函数,而不是去调用其他函数,称为嵌套函数。
def fex(): print("in fex") def fun(): #在fex函数体内声明一个函数fun print("in fun") fun() fex()
变量的作用域:从里往外找,一层一层的的找。
x=0 def test(): x=1 def test1(): x=2 def test2(): x=3 print(x) test2() test1() test() #结果 3
五、装饰器
装饰器实现的条件:高阶函数+嵌套函数 =>装饰器
import time #定义内置函数 def timmer(func): #timmer(test) func=test def fex(): start_time=time.time() func() #run test() stop_time=time.time() print("the func run time is %s" %(start_time-stop_time)) return fex #装饰test函数 @timmer # 相当于test = timmer(test) def test(): time.sleep(3) print("in the test") #直接执行test函数 test() #结果 in the test the func run time is -3.0014219284057617
执行步骤:
- 执行timmer函数,timmer(test) 返回值赋值给test变量,即test=timmer(test)
- 此时的test的值是执行timmer函数返回值fex,即test=fex
- 所以执行test,其实就是执行的是fex函数,test()其实就是执行fex函数。
1、执行函数带参数
import time #定义内置函数 def timmer(func): #timmer(test) func=test def fex(): start_time=time.time() func() #run test() stop_time=time.time() print("the func run time is %s" %(start_time-stop_time)) return fex #装饰test1函数 @timmer # 相当于test1 = timmer(test1) def test1(age,name): print("name:%s,age:%s"%(age,name)) #直接执行test函数 test1() #结果 Traceback (most recent call last): File "/Users/bianbian/PycharmProjects/test/test11.py", line 16, in <module> test1() File "/Users/bianbian/PycharmProjects/test/test11.py", line 6, in fex func() #run test() TypeError: test1() missing 2 required positional arguments: 'age' and 'name'
因为这边执行的test1函数其实就是执行的fex函数,fex函数体内的func()其实就是执行test1函数,但是,test1需要传入name和age两个参数,所以报错。那怎么解决呢?传入参数!
但是你又不能确定传入几个参数,所以用非固定参数传参
import time #定义内置函数 def timmer(func): #timmer(test) func=test def fex(*args,**kwargs):#传入非固定参数 start_time=time.time() func(*args,**kwargs) #传入非固定参数 stop_time=time.time() print("the func run time is %s" %(start_time-stop_time)) return fex #不带参数 @timmer def test1(): # 相当于test1 = timmer(test1) time.sleep(3) print("in the test1") #带参数 @timmer # test2 = timmer(test2) def test2(age,name): print("name:%s,age:%s"%(age,name)) #调用 test1() test2("bianbian",18) #结果 #test1 in the test1 the func run time is -3.0028131008148193 #test2 name:bianbian,age:18 the func run time is -1.5020370483398438e-05
2、执行函数有返回值
如果,被调函数的有返回值??
def timmer(func): #timmer(test) func=test def fex(*args,**kwargs):#传入非固定参数 res=func(*args,**kwargs) #传入非固定参数 return res return fex #不带参数 @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变量。
3、带参数的装饰器
之前装饰器都是没有带参数的,但是访问不到页面时,你用的验证的方式来源不同,这时你该怎么办?
name,password="bianbian","bfj123321" def auth(auth_type):#传递装饰器的参数 print("auth_type",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 name == username and password == password: print("%s has passed authentication"%name) 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") @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() home() bbs()
结果:
可以看出,执行步骤:
- outer_wrapper = auth(auth_type="local")
- home = outer_wrapper(home)
- home()
所以这个函数的作用分别是:
- auth(auth_type) 传递装饰器的参数
- outer_wrapper(func) 把函数当做实参传递进来
- wrapper(*args,**kwargs) 真正执行装饰的函数