文章目录
一、闭包函数
(一)什么是闭包函数
闭包函数=函数嵌套定义+函数对象+名称空间与作用域
闭:指的是该函数是定义在一个函数内部的函数。
包:指的是该函数访问了一个来自外层函数的变量。
(二)为函数体代码传参的两种方式
1.直接使用参数的形式传递
直接通过函数的参数传递,给形参传值。
def func(x): # 设定参数
print(x)
func(1) # 直接传参
func(2)
func(3)
2.闭包函数的形式
通过外层包一个函数,将需要的参数传给它。
def outter():
x = 1111 # 闭函数访问了一个外层函数的变量,是为包。
def inner(): # 定义了一个函数内的函数,是为闭。 这里的inner就是闭包函数的内存地址
print(x)
return inner # 千万不要加括号,否则会执行函数体代码
# 返回闭包函数的内存地址
f = outter()
# print(f) # <function outter.<locals>.inner at 0x000001ACE6E79040>
def f3():
x=222222
f()
f3() # 1111
二、函数的装饰器
(一)什么是装饰器
器:指的是工具。
装饰:指的是为被装饰对象添加额外的功能。
装饰器就是为其他函数添加新功能的函数。
(二)为何要用装饰器
软件开发需要遵循开放封闭原则,装饰器可以在不修改被装饰器对象,即被装饰函数的定义与调用方式都不改变的前提下,为被装饰函数添加新的功能。
开放:指的是对拓展新功能开放。
封闭:指的是对修改源代码封闭。
(三)如何使用装饰器
1.无参装饰器的实现
(1)从定义函数开始,根据需求逐步改进,实现装饰器
①装饰器初探
# 定义函数
import time
def index(x, y):
time.sleep(2)
print("This is index===>",x,y)
index(1,2) # This is index===> 1 2
def home(name):
time.sleep(1)
print("This is page of ==>",name)
home("zhangsan") # This is page of ==> zhangsan
# 实现一个简单的装饰器
# 1) 首先要实现最基本的功能记录时间的功能
# 2) 然后,通过一个闭包函数,实现调用外层函数outter(),返回内层函数wrapper的内存地址
# 3) 把outter()赋值给index,这个index就相当于outter函数的返回值wrapper
# 4) 给index加括号之后,可以触发wrapper函数运行,当wrapper这个局部函数运行时,会寻找它自己的内层函数需要的变量x,
# 5) wrapper函数在定义的时候,确定了参数x是外层变量,所以去外层空间寻找,找到变量值index,由于在调用wrapper的时候,已经传了index需要的两个参数,所以index函数触发调用,
# 6) index层层向上寻找,在全局作用域找到index函数,运行对用的功能,结合wrapper增加的功能,得出了我们需要的结果。但是,这里有一个问题还没有解决,就是这个工具,是针对index定制的,不能用于home等其他函数,需要进行改进。
def outter():
x = index
def wrapper(a,b):
start_time = time.time()
x(a,b)
stop_time = time.time()
print("run time is :%s" %(stop_time-start_time))
return wrapper # 不能加括号,否则会触发函数运行!
index = outter()
index(1,2) # This is index===> 1 2
# run time is :2.000650405883789
②装饰器改进1
# 装饰器改进1
# 1) 因为初步版本的装饰器,受限于传参个数问题,以及无法将函数调用给其他函数使用,所以做了如下改进:
# 2) 通过使用*与**将wrapper函数的形式参数变为可变长度的参数,
# 3) 再把wrapper函数内部的函数x的实际参数变为可变长度参数,这样就实现了,wrapper接收的任何函数,都可以原样传给目标函数x,
# 4) 在wrapper函数的外层,将写死的x=函数名去掉,使用外层函数outter进行接收参数:目标函数名,
# 5) 至此,实现了使用index/home 赋值给outter(index/home),即index/home等同于outter函数的返回值wrapper,加上括号传参后触发运行,由于后面有括号,且可以传任意参数,wrapper函数开始运行,寻找变量x,找到外层函数outter传入的参数,继续运行,最终得出我们想要的结果。
# 6) 但是,仍然还是有问题,装饰后的函数,无法给出与原函数一样的返回值,目前的函数返回值为None,需要继续改进。
import time
def index(x, y):
time.sleep(2)
print("This is index===>",x,y)
return "index的返回值"
def home(name):
time.sleep(1)
print("This is page of ==>",name)
return "home的返回值"
def outter(x):
# x = home
def wrapper(*args,**kwargs):
start_time = time.time()
x(*args,**kwargs)
stop_time = time.time()
print("run time is :%s" %(stop_time-start_time))
return wrapper
index = outter(index)
home = outter(home)
index(1,2) # wrapper(1, 2)
# This is index===> 1 2
# run time is :2.000706195831299
home("zhangsan") # wrapper("zhangsan")
# This is page of ==> zhangsan
# run time is :1.0000085830688477
③装饰器改进2
# 装饰器改进2
# 1) 初步改进版的装饰器函数,无法给出与原函数一样的返回值,所以做了如下改进:
# 2) 先给我们wrapper内的函数赋值,
# 3) 再给wrapper函数一个返回值,就是我们赋值的的那个变量,
# 4) 至此,我们实现了使用outter()给我们想要的函数进行装饰,增加装饰功能的后,不影响原函数的功能,不修改原函数的代码,不改变原函数的调用方式,并且可以输出原函数的结果,以及得出与原函数相同的返回值
# 5) 虽然如次使用在功能上没有问题,但是,每次使用都要先使用与目标函数相同的函数名,去给装饰器函数去赋值并传参目标函数名,然后才可以像装饰目标函数一样调用,显得有些麻烦。
import time
def index(x, y):
time.sleep(2)
print("This is index===>",x,y)
return "index的返回值"
def home(name):
time.sleep(1)
print("This is page of ==>",name)
return "home的返回值"
def outter(x):
# x = home
def wrapper(*args,**kwargs):
start_time = time.time()
res = x(*args,**kwargs)
stop_time = time.time()
print("run time is :%s" %(stop_time-start_time))
return res
return wrapper
index = outter(index) # index=wrapper
home = outter(home) # home=wrapper
res1 = index(1,2) # wrapper(1, 2)
# This is index===> 1 2
# run time is :2.000706195831299
print("====>",res1) # ====> index的返回值
res2 = home("zhangsan") # wrapper("zhangsan")
# This is page of ==> zhangsan
# run time is :1.0000085830688477
print("====>",res2) # ====> home的返回值
④语法糖的使用
# 装饰器改进3 使用语法糖
# 1) 使用语法糖,可以达成如下效果:
# 2) 省略了使用与被装饰函数相同的变量名,给外层函数赋值的过程,这个过程是实现装饰器的关键,即index = outter(index)——>index=wrapper
# 3)
import time
def outter(x):
# x = home
def wrapper(*args,**kwargs):
start_time = time.time()
res = x(*args,**kwargs)
stop_time = time.time()
print("run time is :%s" %(stop_time-start_time))
return res
return wrapper # 千万不能加括号,否则会触发运行
@outter # 替代了 # index = outter(index) # index=wrapper
def index(x, y):
time.sleep(2)
print("This is index===>",x,y)
return "index的返回值"
@outter # 替代了 # home = outter(home) # home=wrapper
def home(name):
time.sleep(1)
print("This is page of ==>",name)
return "home的返回值"
res1 = index(1,2) # wrapper(1, 2)
# This is index===> 1 2
# run time is :2.000706195831299
print("====>",res1) # ====> index的返回值
res2 = home("zhangsan") # wrapper("zhangsan")
# This is page of ==> zhangsan
# run time is :1.0000085830688477
print("====>",res2) # ====> home的返回值
⑤装饰器的一般模板
# 目前阶段的装饰器,可以形成一个模板,给函数提供装饰功能
def outter(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return res
return wrapper
# 装饰器:计算函数运行时间
def timmer(func):
def wrapper(*args, **kwargs):
start=time.time()
res = func(*args, **kwargs)
stop=time.time()
print(stop -start)
return res
return wrapper
# 装饰器:函数登陆认证后可以使用功能
def auth(func):
def wrapper(*args,**kwargs):
name = input("请输入用户名>>>: ").strip()
pwd = input("请输入您的密码>>>:").strip()
if name == "egon" and pwd == "123":
print("用户登录成功")
res = func(*args,**kwargs)
return res
else:
print("账户名或密码错误")
return wrapper
@auth # 替代了 # index = outter(index) # index=wrapper
def index(x, y):
time.sleep(2)
print("This is index===>",x,y)
return "index的返回值"
@auth # 替代了 # home = outter(home) # home=wrapper
def home(name):
time.sleep(1)
print("This is page of ==>",name)
return "home的返回值"
⑥wraps装饰器的使用
# 为了让装饰器更加完美,跟原函数相比更加相似,我们可以对装饰器进一步优化,使用wraps装饰器来装饰我们的wrapper函数,在装饰后的函数值相同的情况下,进一步,让被装饰后的函数的文档注释等等细节属性,跟原函数保持一致,让被装饰器装饰的函数,与原函数无限接近。
from functools import wraps
def timmer(func):
@wraps(func)
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print(stop - start)
return res
return wrapper
@timmer # index = timmer(index) # index = wrapper
def index(x, y):
"""
这是index的文档注释
"""
time.sleep(1)
print("index===>>>",x,y)
return "index的返回值"
index(1,2) # index===>>> 1 2
# 1.0009651184082031
print(index.__name__) # index
help(index) # 查看函数的文档注释
# Help on function index in module __main__:
# index(x, y)
# 这是index的文档注释
(2)完整的无参装饰器模板
from functools import wraps
def outter(func):
@wraps(func)
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
return res
return wrapper
2.叠加多个装饰器
(1)如何同时叠加多个装饰器
在被装饰函数的上方,依次按行进行语法糖操作@装饰器名字。
def deco1(func1): # func1 = wrapper2
def wrapper1(*args, **kwargs):
print('=======>wrapper1')
res1 = func1(*args, **kwargs) #wrapper2==》未完
return res1
return wrapper1
def deco2(func2): # func2 = wrapper3
def wrapper2(*args, **kwargs):
print('=======>wrapper2')
res2 = func2(*args, **kwargs) # wrapper3==》未完
return res2
return wrapper2
def deco3(func3): # func3 = index
def wrapper3(*args, **kwargs):
print('=======>wrapper3')
res3 = func3(*args, **kwargs) # index==》未完
return res3
return wrapper3
# index = wrapper1
@deco1 # deco1(wrapper2)=>wrapper1
@deco2 # deco2(wrapper3)=>wrapper2
@deco3 # deco3(index)=>wrapper3
def index():
print("from index")
return 123
res=index() # res=wrapper1()
# =======>wrapper1
# =======>wrapper2
# =======>wrapper3
# from index
(2)总结:加载顺序与执行顺序
多个装饰器叠加时,自下而上加载,自上而下执行。
import time
def timmer(func):
def wrapper1(*args, **kwargs):
print('===>wrapper1')
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print(stop - start)
return res
return wrapper1
def auth(func):
def wrapper2(*args, **kwargs):
print('===>wrapper2')
name = input("请输入您的账号:").strip()
pwd = input("请输入您的账号:").strip()
if name == "egon" and pwd == "123":
print('登录成功')
res = func(*args, **kwargs)
return res
else:
print("账号密码错误")
return wrapper2
@auth
@timmer
def index():
time.sleep(1)
print("from index")
return 123
# wrapper2=>wrapper1=>index
index() # wrapper1()
3.有参装饰器的实现
(1)什么是有参装饰器
在无参装饰器的基础上,再增加一层外函数,用于传递一个新的参数,这样的三层函数的装饰器就是有参装饰器。
(2)为何要使用有参装饰器
无参装饰器实现了与原函数一样的调用方式,并通过闭包函数实现了原函数参数不被改变,当我们需要一个新的功能,而这个新的功能有需要一个参数的时候,无参装饰器就无法满足装饰器的原则,因此需要有参装饰器去实现这个功能。
# 无参装饰器 两层闭包无法给被装饰函数传递该函数本身需要参数之外的值
import time
def outter(func):
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print(stop - start)
return res
return wrapper
# @函数的内存地址1(参数1,参数2,参数3...) # 语法糖格式@的作用是给函数的内存地址加括号,并把被装饰的函数index当作参数传入装饰器的第二层函数,wrapper就会带着参数进入装饰器执行。
def index(x, y):
print('index===>', x, y)
@outter
def home(name):
print('home====>', name)
(3)如何使用有参装饰器
①示例,演示有参装饰器的实现
# 示范一: 无参装饰器,验证信息的来源无法选择
def outter(func):
def wrapper(*args, **kwargs):
inp_name = input("please input your username: ").strip()
inp_pwd = input("please input your password: ").strip()
with open("db.txt",mode="rt",encoding="utf-8") as f:
for line in f:
name_db, pwd_db, balance_db = line.strip("\n").split(":")
if inp_name == name_db and inp_pwd == pwd_db:
print("login successful")
res = func(*args, **kwargs)
return res
else:
print("login failed")
return wrapper
@outter
def index(x, y):
print('index===>', x, y)
index(1, 2)
# 示范二:增加用户验证数据的多个来源
# ldap
# mysql
# file
def outter2(mode):
def outter(func):
def wrapper(*args, **kwargs):
inp_name = input("please input your username: ").strip()
inp_pwd = input("please input your password: ").strip()
if mode == "file":
print("认证来源=====》file")
with open("db.txt",mode="rt",encoding="utf-8") as f:
for line in f:
name_db, pwd_db, balance_db = line.strip("\n").split(":")
if inp_name == name_db and inp_pwd == pwd_db:
print("login successful")
res = func(*args, **kwargs)
return res
else:
print("login failed")
elif mode == "mysql":
print("认证来源=====》mysql")
elif mode == "ldap":
print("认证来源=====》ldap")
else:
print("未知认证来源")
return wrapper
return outter
outter = outter2(mode="mysql")
@outter #语法糖@触发加括号运行,并将被装饰的函数名作为参数传进去并赋值给被装饰函数名, 实现步骤:@outter===> index=outter(index) ====> index = wrapper
def index(x, y):
print('index===>', x, y)
index(1, 2)
# 示范三:语法糖加载方式简化
def outter2(mode):
def outter(func):
def wrapper(*args, **kwargs):
inp_name = input("please input your username: ").strip()
inp_pwd = input("please input your password: ").strip()
if mode == "file":
print("认证来源=====》file")
with open("db.txt",mode="rt",encoding="utf-8") as f:
for line in f:
name_db, pwd_db, balance_db = line.strip("\n").split(":")
if inp_name == name_db and inp_pwd == pwd_db:
print("login successful")
res = func(*args, **kwargs)
return res
else:
print("login failed")
elif mode == "mysql":
print("认证来源=====》mysql")
elif mode == "ldap":
print("认证来源=====》ldap")
else:
print("未知认证来源")
return wrapper
return outter
@outter2(mode="mysql") # outter2(mode="mysql")就是返回值outter,语法糖@触发加括号运行 outter(index)=index ====》index = wrapper
def index(x, y):
print('index===>', x, y)
index(1, 2) # wrapper(1, 2)
②完整的有参装饰器模板
def outter2(x, y, z, a, b):
def outter1(func):
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
return res
return wrapper
return outter1