闭包函数
什么是闭包
闭包:闭是封闭(函数内部函数),包是包含(该内部函数对外部作用域而非全局作用域的变量的引用)。闭包指的是:函数内部函数对外部作用域而非全局作用域的引用。
两种为函数传值的方式
为函数传参的方式一:使用参数的形式
def func(x):
print(x)
func(1)
为函数传参的方式二:包给函数
def outter(x):
def inner():
print(x)
return inner
f = outter(1)
f()
f()
f()
# 查看闭包的元素
print(F"f.__closure__[0].cell_contents: {f.__closure__[0].cell_contents}")
闭包函数的应用
闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。
应用领域:延迟计算(原来我们是传参,现在我们是包起来)、爬虫领域。
import requests
def outter(url):
def get():
response = requests.get(url)
print(f"done: {url}")
return get
baidu=outter('https://www.baidu.com')
python = outter('https://www.python.org')
baidu()
baidu()
python()
python()
装饰器
无参装饰器
什么是装饰器
器指的是工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰器对象添加额外功能。因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加额外的功能。
需要注意的是:
- 装饰器本身其实是可以任意可调用的对象
- 被装饰的对象也可以是任意可调用的对象
为什么用装饰器
如果我们已经上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以使用装饰器。因为软件的维护应该遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能指的是开放的。
装饰器的实现必须遵循两大原则:
- 不修改被装饰对象的源代码
- 不修改被装饰对象的调用方式
装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。
import time
def index():
print('welcome to index')
time.sleep(1)
def time_count(func):
# func = 最原始的index
def wrapper():
start = time.time()
func()
end = time.time()
print(f"{func} time is {start-end}")
return wrapper
# f = time_count(index)
# f()
index = time_count(index) # index为被装饰函数的内存地址,即index = wrapper
index() # wrapper()
welcome to index
<function index at 0x102977730> time is -1.0038220882415771
完善装饰器
上述的装饰器,最后调用index()的时候,其实是在调用wrapper(),因此如果原始的index()有返回值的时候,wrapper()函数的返回值应该和index()的返回值相同,也就是说,我们需要同步原始的index()和wrapper()方法的返回值。
import time
def index():
print('welcome to index')
time.sleep(1)
return 123
def time_count(func):
# func = 最原始的index
def wrapper():
start = time.time()
res = func()
end = time.time()
print(f"{func} time is {start-end}")
return res
return wrapper
index = time_count(index)
res = index()
print(f"res: {res}")
welcome to index
<function index at 0x102977620> time is -1.0050289630889893
res: 123
如果原始的index()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于有wrapper()=index(),所以给wrapper()方法传参即可。
import time
def index():
print('welcome to index')
time.sleep(1)
return 123
def home(name):
print(f"welcome {name} to home page")
time.sleep(1)
return name
def time_count(func):
# func = 最原始的index
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print(f"{func} time is {start-end}")
return res
return wrapper
home = time_count(home)
res = home('egon')
print(f"res: {res}")
装饰器语法糖
在被装饰函数正上方,并且是单独一行写上@装饰器名
import time
def time_count(func):
# func = 最原始的index
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print(f"{func} time is {start-end}")
return res
return wrapper
@time_count # home = time_count(home)
def home(name):
print(f"welcome {name} to home page")
time.sleep(1)
return name
@time_count # index = time_count(index)
def index():
print('welcome to index')
time.sleep(1)
return 123
res = home('egon')
print(f"res: {res}")
welcome egon to home page
<function home at 0x102977620> time is -1.0005171298980713
res: egon
装饰器模板
def deco(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return res
return wrapper
有参装饰器
无参装饰器只套了两层,本节将讲一个套三层的装饰器——有参装饰器,但现在我们先实现一个用户登录注册的装饰器。
import time
current_user = {'username': None}
def login(func):
# func = 最原始的index
def wrapper(*args, **kwargs):
if current_user['username']:
res = func(*args, **kwargs)
return res
user = input('username: ').strip()
pwd = input('password: ').strip()
if user == 'nick' and pwd == '123':
print('login successful')
current_uesr['usre'] = user
res = func(*args, **kwargs)
return res
else:
print('user or password error')
return wrapper
@login
def home(name):
print(f"welcome {name} to home page")
time.sleep(1)
return name
@login
def index():
print('welcome to index')
time.sleep(1)
return 123
res = index()
username: nick
password: 123
login successful
welcome to index
对于上面的登录注册,我们把用户登录成功的信息写入内存当中。但是在工业上,用户信息可以存在文本中、mysql中、mongodb当中,但是我们只让用户信息来自于file
的用户可以认证。因此我们可以改写上述的装饰器。
import time
current_user = {'username': None}
def login(func):
# func = 最原始的index
def wrapper(*args, **kwargs):
if current_user['username']:
res = func(*args, **kwargs)
return res
user = input('username: ').strip()
pwd = input('password: ').strip()
engine = 'file'
if engine == 'file':
print('base of file')
if user == 'nick' and pwd == '123':
print('login successful')
current_uesr['usre'] = user
res = func(*args, **kwargs)
return res
else:
print('user or password error')
elif engine == 'mysql':
print('base of mysql')
elif engine == 'mongodb':
print('base of mongodb')
else:
print('default')
return wrapper
@login
def home(name):
print(f"welcome {name} to home page")
time.sleep(1)
@login
def index():
print('welcome to index')
time.sleep(1)
res = index()
username: nick
password: 123
base of file
login successful
welcome to index
三层闭包
现在需求改了,我们需要判断用户动态的获取用户密码的方式,如果是file
类型的,我们则让用户进行认证。因此我们可以使用有参装饰器。
import time
current_uesr = {'username': None}
def auth(engine='file'):
def login(func):
# func = 最原始的index
def wrapper(*args, **kwargs):
if current_user['username']:
res = func(*args, **kwargs)
return res
user = input('username: ').strip()
pwd = input('password: ').strip()
if engine == 'file':
print('base of file')
if user == 'nick' and pwd == '123':
print('login successful')
current_uesr['usre'] = user
res = func(*args, **kwargs)
return res
else:
print('user or password error')
elif engine == 'mysql':
print('base of mysql, please base of file')
elif engine == 'mongodb':
print('base of mongodb, please base of file')
else:
print('please base of file')
return wrapper
return login
@auth(engine='mysql')
def home(name):
print(f"welcome {name} to home page")
time.sleep(1)
@auth(engine='file')
def index():
print('welcome to index')
time.sleep(1)
res = index()
username: nick
password: 123
base of file
login successful
welcome to index
由于两层的装饰器,参数必须得固定位func
,但是三层的装饰器解除了这个限制。我们不仅仅可以使用上述单个参数的三层装饰器,多个参数的只需要在三层装饰器中多加入几个参数即可。也就是说装饰器三层即可,多加一层反倒无用。