闭包
装饰器的基础就是闭包。闭包首先是嵌套函数,并且内层函数对外层函数变量非全局变量有引用。
def wrap(x):
def inner():
print('start')
print(x)
print('end')
return inner
_inner = wrap(2)
_inner()
_inner得到了内层函数的内存地址,形参x就是外层函数的变量。闭包的一个特性就是外层函数结束了,内存函数依然可以被调用。
装饰器
装饰器从字面意思上理解就是装饰用的,在python中就是给原函数加功能的,相当于装饰。在给函数加功能的时候不改变被装饰函数的代码和调用方式。在游戏中,有一个打的功能,需要给打的功能增加一个击退效果。用这个简单模拟下装饰器的使用方式。
被装饰函数:
def attack():
print('这里是打,就是这么简单')
attack()
版本一:
def attack():
print('这里是打,就是这么简单')
print('这里是击退')
attack()
#代码执行效果
这里是打,就是这么简单
这里是击退
版本一,能实现这个效果,但是它改变了原函数的代码,新增了一行打的代码,不符合装饰器的要求。
版本二:
def attack():
print('这里是打,就是这么简单')
def wrap(x):
def inner():
x()
print('这里是击退')
return inner
_attack = wrap(attack) #这里_attack可以取名为attack
_attack() #同样可以attack调用
#执行效果
这里是打,就是这么简单
这里是击退
版本二应用了闭包,将被装饰函数当作参数传入内层函数调用。被装饰函数attack内部代码没有被改变,同样实现了效果。但是这里函数的调用改变了,原来attack()变成了_attack()。
是的,我们取名字的时候可以_attack()变成attack()不就行了。这样又不改变被装饰函数,又不改变调用方式。这就是一个装饰器。attack = wrap(attack)可以用@wrap表示,@加上装饰器函数名。@wrap一定要在被装饰函数的上面连在一起。哪个函数,比如闪现需要击退,就在闪现函数的上面加这一行就可以实现闪现击退效果。
def wrap(x):
def inner():
x()
print('这里是击退')
return inner
@wrap #attack = wrap(attack)
def attack():
print('这里是打,就是这么简单')
attack()
#执行效果
这里是打,就是这么简单
这里是击退
算是完成了一个装饰器。但是attack函数是没有任何返回值和参数的,那带有返回值和参数的怎么装饰呢
版本三:装饰带返回值的函数,假入attack()函数每次打都会返回一个True,:
def attack():
print('这里是打,就是这么简单')
return True
带返回值装饰器:
def wrap(x):
def inner():
result = x() #result得到了attack()的返回值True
print('这里是击退')
return result #返回True
return inner
@wrap #attack = wrap(attack)
def attack():
print('这里是打,就是这么简单')
return True #被装饰器函数返回一个True
print(attack()) #这里attack得到的是inner函数的内存地址,所有attack()的返回值通过inner的返回值传回。
#执行结果
这里是打,就是这么简单
这里是击退
True
上面已经实现了带返回值的装饰器。如果被装饰的函数带有参数呢,比如attack(a,b,c),并且击退效果不仅仅装饰打的动作,还可以装饰闪现功能,闪现功能的参数数量和attack的数量不一样,怎么装饰呢
版本四:带有参数的函数的装饰器
def wrap(x):
def inner(*args,**kwargs):
result = x(*args,**kwargs)
print('这里是击退')
return result
return inner
@wrap #attack = wrap(attack)
def attack(name): #这里定义了需要传入一个参数
print(f'这里{name}是打,就是这么简单')
return True
attack('lg') #这里lg被传给了inner函数,inner函数最终将lg转交给了上面的x(*args,**kwargs)
#执行结果
这里lg是打,就是这么简单
这里是击退
版本四就完成了一个可以装饰传入参数,也可以有返回值的装饰器。