装饰器:本质是函数
功能:装饰其他函数(为其他函数添加附加功能)
装饰器原则:
1、不能修改被装饰函数的源代码
2、不能修改被装饰函数的调用方式
装饰器相对于源代码而言就是个小透明,没有修改源代码,也不影响源代码的运行。
在不修改源代码的情况下,为其添加新的功能;
函数即变量
简单举例:
x = 1
y = x
def test():
print("in the text")
- 相当于在内存中创建一个空间,里面存储 1,门牌号是x
- y 同样指向 x 门牌号所代表的空间,y 同样也是个门牌号
- 函数test() 也同样创建一个空间,里面存储函数体(print(“in the text”)),门牌号为test ,加上括号即可调用
test只是一个门牌号
print(test)
>> <function test at 0x00000000052340D0>
高阶函数
高阶函数满足:
- 把一个函数名(内存地址)作为实参传给另一个函数
- 返回值中包含函数名
例如:
import time
def bar():
time.sleep(3) #延时3秒输出
print("in the bar")
def deco(func): #用来计时
start_time = time.time()
func()
stop_time = time.time()
print("the func run time is %s" %(stop_time-start_time))
#bar()
deco(bar)
**注:**deco()具有装饰器的作用,但不是装饰器,因为不符合装饰器原则2
改变了原本 bar() 调用方式
思考:如何不改变原函数的调用方式而实现添加计时功能呢?
#可以将test()添加返回值,返回原地址;
def deco(func): #用来计时
start_time = time.time()
func()
stop_time = time.time()
print("the func run time is %s" %(stop_time-start_time))
return func
bar = deco()
bar() #此时,便可以不改变原函数的调用方式了
嵌套函数
函数也类似变量,分为全局函数和局部函数
定义:在一个函数体内,用 def 去声明另一个函数,不能在外部直接调用
x = 0
def grandpa():
x = 1
def father():
x = 2
def son():
x = 3
print(x)
son()
father()
grandpa()
逐级嵌套,逐级调用。
装饰器案例及分析
高阶函数 + 嵌套函数 ==> 装饰器
装饰器函数
接上述例子:
import time
def timer(func):
def deco(): # 用来计时
start_time = time.time()
func()
stop_time = time.time()
print("the func run time is %s" % (stop_time - start_time))
return deco
def bar(): # 延时3秒,打印输出内容
time.sleep(3)
print("in the bar")
bar = timer(bar)
bar()
- 首先程序会先执行 bar = timer(bar) ,进入timer 函数内
- 然后传 bar 进入函数后直接返回该 bar 地址作为 deco,赋给bar
- 接着执行bar 函数,会执行timer内部潜逃的deco()函数
进一步优化
进一步优化:取消 bar = timer(bar) 这一过程
#想装饰哪个,就加在哪个的开头
@timer #此步直接执行了 bar = timer(bar)
def bar(): # 延时3秒,打印输出内容
time.sleep(3)
print("in the bar")
bar()
使装饰器通用
再进一步:使装饰器通用,同时装饰输入参数数量不同的函数;
不同的函数统一加上计时功能,观察不同的函数运行时间:
import time
def timer(func):
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
@timer #test1 = timer(test1)
def test1(): # 延时3秒,打印输出内容
time.sleep(1)
print("in the test1")
@timer
def test2(name, age): #test2(name) = deco(name)
print("test2:",name)
test1()
test2("Evan",22)
#test2()有参数传入,相当于deco()有参数传入,需要定义形参(不定参数)
不知道有没有参数传入,传入多少个参数
装饰器的使用
加入验证功能
目标:共有100个函数,要给其中20个加入验证功能
import time
#装饰器
def auth(func):
def wrapper(*args,**kwargs):
username = input("Username:".strip())
password = input("password:".strip())
if user == username and passwd == password:
print("\033[32:1mUser login succeeded\033[0m")
func(*args,**kwargs) #(1)
else:
exit("\033[31:1mInvalid username or password\033[0m")
return wrapper
def index():
print("Welcome to index page")
@auth
def home():
print("Welcome to homepage")
@auth
def bbs():
print("Welcome to bbs page")
目标功能实现。
解决返回值问题
再进一步考虑:如果被修饰的函数是有返回值的,例如:
def index():
print("Welcome to index page")
@auth
def home():
print("Welcome to homepage")
return "from home"
@auth
def bbs():
print("Welcome to bbs page")
home()
#此时执行home() = 执行wrapper(),但返回值丢失了
#因为在执行到上述(1)步时,得到的返回值并没有使用,没有输出出来
所以改进,在(1)处再加上返回值
def auth(func):
def wrapper(*args,**kwargs):
username = input("Username:".strip())
password = input("password:".strip())
if user == username and passwd == password:
print("\033[32:1mUser login succeeded\033[0m")
res = func(*args,**kwargs) #(1)
print("----after login----")
return res
else:
exit("\033[31:1mInvalid username or password\033[0m")
return wrapper
在 (1)处再加入返回值,即可实现返回值不丢失。
改进认证方式
再进一步,如果要将两个登陆用户名密码分别采用不同的验证方式:
- home()采用本地文件认证
- bbs()采用远程ldap(统一认证管理)认证
解决方式:
- @auth定义时传入参数:例如@auth(auth_type = “local”);
- 装饰器函数再嵌套一层
import time
user, passwd = "Evan", "0000"
def auth(auth_type):
print("auth func:",auth_type)
def out_wrapper(func):
def wrapper(*args, **kwargs):
print("wrapper func args:", *args, **kwargs)
if auth_type == "local":
username = input("Username:".strip())
password = input("password:".strip())
if user == username and passwd == password:
print("\033[32;1mUser login succeeded\033[0m")
res = func(*args, **kwargs) # (1)
print("----after login----")
return res
else:
exit("\033[31;1m Invalid username or password \033[0m")
elif auth_type == "ldap":
print("LDAP验证")
return wrapper
return out_wrapper
def index():
print("Welcome to index page")
@auth(auth_type="local") #home = wrapper
def home():
print("Welcome to homepage")
return "from home"
@auth(auth_type="ldap")
def bbs():
print("Welcome to bbs page")
index()
home()
bbs()
以 home() 为例:
- 最开始传入 auth(auth_type) 的参数为local。。。auth_type:”local”
- 然后定义out_wrapper()函数,但并未进入内部,return out_wrapper
- 即 home = wrapper
- 再在wrapper内进行判断,是 local 还是 ldap 验证.