闭包函数
- 1.什么是闭包函数
内部函数包含对外部作用域而非全局作用域的引用。作用域的关系在函数定义阶段就已经被固定,与调用位置无关;在任意位置调用函数都要跑到定义函数时的位置寻找作用域关系。
例:
def f1():
x = 1
def inner():
print(x)
return inner
func = f1()
def f2():
x = 11111
func()
f2()
运行结果为 : x = 1
- 闭包函数:
def outter():
x = 1
def inner():
print(x)
return inner
f = outter()
def f2():
x = 1111
f()
f2()
def f3():
x = 222
f()
f3()
f2 和f3的运行结果均为 x = 1
- 为函数体传值的方式之一:使用参数的方式传值
def inner(x):
print(x)
inner(1)
inner(11)
- 为函数体传值的方式之二:使用闭包方式
def outer(x):
def inner():
print(x)
return inner
f = outter(222)
f()
- 闭包的意义与应用
- 意义:返回的函数对象,不仅是单独一个对象,还将外层包裹这个函数的作用域一起返回,这使得,该函数在何处调用都优先使用自己外层包裹的作用域。
例:
import requests
def get(url):
response = requests.get(url)
if response.status_code == 200:
print(response.text)
get('http://www.baidu.com')
get('http://www.python.org')
利用闭包函数的特性改进如下:
import requests #调用requests模块应该先安装此模块,在Windows命令行输入:pip install requests
def outter(url):
def get():
response = requests.get(url)
if response.status_code == 200:
print(response.text)
return get
baidu = outter('https://www.baidu.com')
python = outter('https://www.python.org')
baidu()
python()
装饰器
- 什么是装饰器:
通俗来讲,装饰器就是闭包函数的一种应用场景。开放封闭原则:对修改封闭,对扩展开放。装饰器本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
强调装饰器的原则:1.不修改装饰对象的源代码;2.不修改被装饰对象的调用方式。
装饰器目标:在遵循1和2的前提下,为被装饰对象添加新功能。
- 如何使用装饰器:
例:
import time
def index():
start = time.time()
print('this is index')
time.sleep(3)
stop = time.time()
print('run time is %s '%(stop - start))
index()
我们将上述代码进行第一次改进:
import time
def index():
print('this is index')
time.sleep(3)
def f2():
print('from f2')
time.sleep(2)
start = time.time()
index()
stop = time.time()
print('run time is %s '%(stop - start))
若要获取f2的运行时间也是一样:
start = time.time()
f2()
stop = time.time()
print('run time is %s'%(stop - start))
这种改进方式不合理,每次调用都需要重复写代码,我们继续改进:
Import time
def index():
print('this is index')
time.sleep(3)
def timmer(func):
start = time.time()
func()
stop = time.time()
print('run time is %s'%(stop - start))
timmer(index)
我们发现还是不合理,index被固定住了,timmer这个装饰器仅能用来装饰index,继续改进:
import time
def index():
print('this is index')
time.sleep(3)
def timmer(func):
def inner():
start = time.time()
func()
stop = time.time()
return inner
f = timmer(index)
f()
上述改进稍微好了点,但是不符合装饰器的两大原则之一(我们改变了原函数的调用方式),于是有以下代码
import time
def index():
print('this is index')
time.sleep
def timmer(func):
def wrapper():
start = time.time()
func()
stop = time.time()
print('run time is %s'%(stop - start))
return wrapper
index = timmer(index) # index = wrapper函数的内存地址
index()
至此,我们的本着装饰器的两大原则将以上功能实现了,但是问题随之而来,如果,index函数有返回值呢,如果另外一个函数需要用到timmer这个装饰器,且带有参数,以上的修改就不能满足了。
我们继续改进如下:
import time
def index():
print('this is index')
time.sleep(3)
return 456
def home(name):
print('welcome %s to home page'%name)
time.sleep(2)
def timmer(func):
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **keargs)
stop = time.time()
print('run time is %s'%(stop - start))
return res
return wrapper
index = timmer(index)
home = timmer(home)
res = index()
home = ('zhangsan')
至此,我们的修改将近完善了,python内置了更简洁的方法,如下:
import time
def time(func):
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print('run time is %s'%(stop - start))
return res
return wrapper
@timmer #等同于index = timmer(index)
def index():
print('this is index')
time.sleep(3)
return 234
@timmer #等同于home = timmer(home)
def home(name):
print('welcome %s to home page'%name)
time.sleep(2)
res = index()
home('zhangsan')
上述@timmer写法在python中叫‘语法棒棒糖’在被装饰对象的正上方,并且是单独一行写上@装饰器名
- 有参装饰器
介绍了对带参数的函数和有返回值的函数进行装饰,那么有没有带参数的装饰器呢,如果有,该怎么用呢,我们通过以下一段代码来看:(我们假设做一个登录验证的小功能)
import time
current_user = {'user': None}
def deco(func):
def wrapper(*args, **kwargs):
if current_user['user']:
res = func(*args, **kwargs)
return res # 已经登录过,无需再输入用户名和密码
user = input('username>>: ').strip()
pwd = input('password>>: ').strip()
if user == 'zhangsan' and pwd == '123' : # 我们在这里假设数据库里面的一位用户的账号密码是这样
print('login sucessful') #记录用户登录状态
current_user = user
res = func(*args, **kwargs)
return res
else:
print('username or password error')
return wrapper
@deco
def index():
print('welcome to index page')
time.sleep(0.5)
@deco
def home(name):
print('welcome %s to home page'%name)
time.sleep(0.5)
index()
home('zhangsan')
我们很快就发现上面写的有些不合理,若是用户有数据库方面的验证,还有一些别的数据文件验证方式呢,于是我们改进如下:
import time
current_user = {'user':None}
def auth(engine = 'file'):
def deco(func):
def wrapper(*args, **kwargs):
if current_user['user']: #记录登录状态。
res = func(*args, **kwargs)
return res
user = input('username>>: ').strip()
pwd = input('password>>: ').strip()
if engine == 'file' : #假设基于文件认证
if user == 'zhangsan' and pwd == '123' :
print('login sucessful')
current_user['user'] = user #记住登录状态
res = func(*args, **kwargs)
return res
else:
print('username or password error')
elif engine == 'mysql' : #代码我这边就先不写了 -_-||
print('mysql')
else:
print('etc')
return wrapper
return deco
@auth(engine = 'file')
def index():
print('welcome to index page')
time.sleep(1)
@auth(engine = 'file')
def home(name)
print('welcome to %s home page'%name)
time.sleep(1)
index()
home('zhangsan')
简单理解,带参数的装饰器就是在原闭包的基础上又加上一层闭包,好处是针对不同参数做不同的处理。
那介绍了这么多,如果在实际应用中针对每个类别都需要写个装饰器的话,估计累死了。有没有通用的万能装饰器呢,看如下代码:
def outer(func):
def inner(*args, **kwargs)
res = func(*args, **kwargs)
return res
return inner
@outer
def test():
print('a test')
@outer
def test2():
print('another test')
return 'python'
@outer
def test3(x):
print('last test for %s'%x)
test()
test2()
test3()
把上面几种示例结合起来,就完成了通用装饰器的功能,原理都同上,就不过多废话了。