闭包函数与装饰器
一、闭包函数
闭指的是:该函数是一个内部函数
包指的是:指的是该函数包含对外部作用域(非全局作用域)变量的引用
闭包函数:内嵌函数包含对外部函数作用域(非全局作用域)中变量的引用,那么这个内嵌函数就是闭包函数。
def outter():
x = 1
def inner():
print(x)
return inner
f=outter()
f()
print(f.__closure__)#__closure__可以获取函数的闭包属性
print(f.__closure__[0].cell_contents)#打印闭包函数包含的外部变量值
<<<1
<<<(<cell at 0x0000027935296EE0: int object at 0x00000279037A6930>,)
<<<1
闭包函数的使用:
避免重复传参
hobbies={'李明':'swim','赵四':'sing','刘能':'扯犊子'}
def get_hobby(name):
print(hobbies[name])
#如果要多次得到刘能的爱好必须多次传参
get_hobby(['刘能'])
get_hobby(['刘能'])
def get_hobby(name):
def inner():
print(hobbies[name])
return inner
#传一次参就可以重复得到刘能的爱好
hobby=get_hobby('刘能')
hobby()
hobby()
给函数传固定的值
def hobby_of_liuneng():
hobby='扯犊子'
def inner():
print(hobby)
return inner
hobby=hobby_of_liuneng()
hobby()
<<<'扯犊子'
二、装饰器
装饰指的是为被装饰器对象添加额外功能。
定义装饰器通常是定义一个函数,只不过该函数的功能是用来为其他函数添加额外的功能。当然类也是可以做装饰器的,同时装饰器也是可以装饰类的,不过这不常见。
为什么要用装饰器:软件的维护应该遵循开放封闭原则,开放封闭原则指的是:软件一旦上线运行后对修改源代码是封闭的,对扩展功能的是开放的。这就要用到装饰器了。
装饰器的实现必须遵循两大原则:
- 不修改被装饰对象的源代码
- 不修改被装饰对象的调用方式
1.无参装饰器
如何要实现一个统计函数运行时间的函数,传统的方法可以这样实现:
import time
def func():
time.sleep(2)
print('from func')
def count_time(func):
start=time.time()
func()
end=time.time()
print(end-start)
count_time(func)
以上的方法虽然实现了统计函数运行时间的功能,但是要统计func函数的时间需要调用count_time函数。如果我现在想要一种调用func函数就能统计其运行时间的方法,可以怎么写呢
import time
def func():
time.sleep(2)
print('from func')
def count_time(func):
def wrapper():
start=time.time()
res=func()
end=time.time()
print(end-start)
return res
return wrapper
func=count_time(func)
func()
以上代码可以在不修改被装饰对象源代码以及调用方式的基础上为其加上统计运行时间的功能,这也就是一个无参装饰器。但是如果传入的func函数带有参数的话,又该如何写呢
import time
def func(n):
time.sleep(n)
print('from func')
def count_time(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
end=time.time()
print(end-start)
return res
return wrapper
func=count_time(func)
func(2)
count_time中的*args,**kwargs会将位置参数和关键字参数转换为元组和字典传入func并再次打散为位置参数和关键字参数。这样就实现了将被装饰对象func的任意参数传入装饰器的功能了。
python中提供了一种语法糖的形式代替func=count_time(func)这行代码:@count_time
import time
def count_time(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
end=time.time()
print(end-start)
return res
return wrapper
@count_time
def func(n):
time.sleep(n)
print('from func')
func(2)
当python解释器运行至@count_time处,实质上解释器已经帮你将func传入count_time函数并返回wrapper函数的地址,当调用func(2)时本质上调用的是传入func值以后的wrapper函数。
def count_time(func):
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print(end - start)
return res
print('from wrapper')
return wrapper
@count_time
def func(n):
time.sleep(n)
print('from func')
<<<'from wrapper'
如下方代码所示,当出现多个装饰器装饰一个函数时就相当于a=deco3(deco2(deco1(a)))
@deco3
@deco2
@deco1
def a():
pass
2.有参装饰器
如果我现在想写一个用户认证功能,用装饰器可以怎么写呢
def authorized(func):
def wrapper(*args, **kwargs):
if engine=='file':
func()
if engine=='mysql':
func()
return wrapper
如果要判断认证用户的方式是从文件中读取数据进行认证呢,还是从mysql数据库中读取数据进行认证呢,显然还需要给装饰器传入一个参数
def authorized(engine):
def deco(func):
def wrapper(*args, **kwargs):
if engine=='file':
func()
if engine=='mysql':
func()
return wrapper
return deco
def func():
pass
f=authorized('file')
func=f(func)
func()
如此一来就实现的有参装饰器,同样的上面的代码可以改为语法糖的形式
def authorized(engine):
def deco(func):
def wrapper(*args, **kwargs):
if engine=='file':
func()
if engine=='mysql':
func()
return wrapper
return deco
@authorized('file')
def func():
pass
同样的@authorized(‘file’)这行代码会运行装饰器外面的两层函数,返回wrapper函数的地址赋给func变量。