一,闭包
二,*args 和 **kwargs
三,无参装饰器实现
知识储备,需要了解名称空间,闭包函数,参数传递(*args和**kwargs的用法)
一,闭包函数
想要实现装饰器,那么首先就需要了解什么是闭包,闭包函数简单来说就是一个函数中嵌套一个函数,其中内部嵌套的函数能调用外部函数传递的参数。光说概念难以理解,那么上代码。
def func(x):
def func1():
y = 10;
print(x + y)
return func1
outer = func(10)
outer()
----------------
>>>20
这里可以看到func1函数是嵌套在func函数中的,同时调用函数func的时候返回的是函数func1的函数名的内存地址,这样用变量名outer这个变量名来接收,那么是不是这个outer 变量名的值就是该返回的函数名的内存地址?同时我们知道,在python中,函数名加()后才表示开始调用函数体,所以现在的格式就相当于“func1的内存地址()”来调用函数体,同时变量outer和函数名func1在名称空间中也并不冲突,这也就导致了在调用的时候并不会导致冲突。
二,*args 和 **kwargs
在Python中的代码中经常会见到这两个词 args 和 kwargs,前面通常还会加上一个或者两个星号。其实这只是编程人员约定的变量名字,args 是 arguments 的缩写,表示位置参数;kwargs 是 keyword arguments 的缩写,表示关键字参数。这其实就是 Python 中可变参数的两种形式,并且 *args 必须放在 **kwargs 的前面,因为位置参数在关键字参数的前面。
*args 的用法:
在给函数传递参数的时候有时并不是很确定需要传递多少个位置参数的时候,这个时候就需要*args,*args会将传给函数体的多出来的位置参数整合成一个元组,然后传递给函数体。
def func(x,y,*args):
print(x)
print(y)
print(args)
print(type(args))
func(1,2,3,4,5,6) # 这里传递多个参数给函数体
# 1
# 2
# (3 4 5 6)
# <class 'tuple'>
这里为其传递多个参数,那么按照位置参数将实参传递为形参,但是从上面的例子可以看出来,3,4,5,6这个实参并没有对应的位置参数,那么这个时候*args就将这些没有对应的位置形参的实参整合成一个元组(3,4,5,6)。
**kwargs:
其实**kwargs跟*args的用法基本上是一样的,不同点在于**kwargs会将多出来的关键字实参整合成一个字典。
def func(x = 1,y = 2,**kwargs):
print(x)
print(y)
print(kwargs)
print(type(kwargs))
func(x = 111,y = 222,a = 333,b = 444,c =555) # 这里关键字参数a,b,c 是多出来的关键字实参
# 111
# 222
# {'a': 333, 'b': 444, 'c': 555}
# <class 'dict'>
三,无参装饰器实现:
其实装饰器就是一个闭包函数,装饰器是闭包的一种应用。什么是装饰器呢,简言之,python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
下面以测试代码的执行时间为例进行讲解如何实现装饰器。
import time
def index(x,y):
print('from index %s %s'%(x,y))
def home (name):
print('welcome %s to home',name)
#正常情况下调用函数
index(11,22)
现在的问题是,我想要调用出执行这两个函数体各执行了多少时间,有人会想这还不简单?然后代码直接出来了
import time
def index(x,y):
start = time.time()
time.sleep(3) # 这里让程序睡上3秒,不然的话程序执行的会太快了
print('from index %s %s'%(x,y))
stop = time.time()
print(stop - start)
def home (name):
start = time.time()
time.sleep(3)
print('welcome %s to home',name)
stop = time.time()
print(stop - start)
index(11,22)
home('egon')
这样解决问题了吗?解决了,但是是不是存在问题?难道每次我一提出问题都要像这样来改动代码吗?这样的话未免也太low了吧。那么装饰器就登场了,装饰器就是在不更改原函数代码的基础上进行扩展函数的功能。
import time
def index(x,y):
print('from index %s %s'%(x,y))
def home (name):
print('welcome %s to home',name)
def timeer(func): # 闭包函数的应用
def wrapper(*args,**kwargs):
start = time.time()
time.sleep(3)
func(*args,**kwargs)
stop = time.time()
print(stop - start)
return wrapper
index = timeer(index) # 此时的变量index的值就是wrapper的内存地址
print(index)
index(11,22) # 在外部使用者看来相当于是调用index
这样一个装饰器基本上是写好了,但是还是存在一个问题,那就是当我们写的函数存在返回值时,那么是否会同样打印出我们想要的值呢?
import time
def index(x,y):
print('from index %s %s'%(x,y))
return 111
def home (name):
print('welcome %s to home',name)
return 'ok'
def outter(func):
def wrapper(*args,**kwargs):
start = time.time()
time.sleep(3)
func(*args,**kwargs)
stop = time.time()
print(stop - start)
return wrapper
index = outter(index)
res = index(11,22)
print(res)
# from index 11 22
# 3.001466989517212
# None
home = outter(home)
ret = home('egon')
print(ret)
# welcome %s to home egon
# 3.0004019737243652
# None
可以看到的是,被装饰函数是存在返回值的,但是经过装饰器后,并没有打印出返回值,也就是说经过装饰器装饰后,没有返回值,那么就证明我们的装饰器写的还不是很成熟。那么进行优化。
import time
def index(x,y):
print('from index %s %s'%(x,y))
return 111
def home (name):
print('welcome %s to home',name)
return 'ok'
def outter(func):
def wrapper(*args,**kwargs):
start = time.time()
time.sleep(3)
# 既然你需要返回值那么简单啊,直接来一个变量来接收不就好了
res = func(*args,**kwargs)
stop = time.time()
print(stop - start)
return res
return wrapper
index = outter(index)
res = index(11,22)
print(res)
# from index 11 22
# 3.0038366317749023
# 111
home = outter(home)
ret = home('egon')
print(ret)
# welcome %s to home egon
# 3.001862049102783
# ok
那么这样的话这个装饰器就基本上写成功了。但是这样看是不是总有那么一点,调用上的麻烦?
那么在进行优化。
这里需要知道的是python中存在一种语法糖,这样能十分简化代码。
#在被装饰对象正上方单独一行加上@装饰器名字
# 需要知道的是写完的装饰器必须在被装饰对象之上,不然就会报错
import time
def outter(func):
def wrapper(*args,**kwargs):
start = time.time()
time.sleep(3)
# 既然你需要返回值那么简单啊,直接来一个变量来接收不就好了
res = func(*args,**kwargs)
stop = time.time()
print(stop - start)
return res
return wrapper
@outter # 相当于实现index = outter(index)
def index(x,y):
print('from index %s %s'%(x,y))
return 111
@outter # 相当于实现home = outter(home)
def home (name):
print('welcome %s to home',name)
return 'ok'
res = index(11,22)
print(res)
ret = home('egon')
print(ret)
那么这样就实现了装饰器。