在这个例子中我们定义了一个 status 函数,status 接收一个函数名然后直接返回该函数名。这样我们实现了不修改原函数 name,并且添加了一个新功能的需求。但是这里有个缺陷就是函数的调用方式改变了。即不是原本的 name,而是 temp。
要解决这个问题很简单,相信 a = a*3 这样的表达式大家都见过,那么上述代码中的 temp = status(name) 同样可以修改为 name = status(name),这样我们就完美的解决了问题:既添加新功能又没有修改原函数和其调用方式。修改后的代码如下:
def status(func):
print('慌的一逼!') return funcdef name():
print('我是梅西!')
name = status(name)
name()
复制代码
但这样的代码却有个不便之处,即每次使用这样的装饰器,我们都要写类似 name = status(name) 的代码。程序员都是懒的,所以才有那么多高级的语法。在 python 中为了简化这种情况,提供了一个语法糖 @,在每个被装饰的函数上方使用这个语法糖就可以省掉这一句代码 name = status(name),最后的代码如下:
def status(func):
print('慌的一逼!') return func@statusdef name():
print('我是梅西!')
name()
复制代码
这样我们就弄清楚了装饰器的工作原理:
1 写一个高阶函数,即参数是函数,返回的也是函数。
2 在利用语法糖@,简化赋值操作。
但是对比开头的例子,还是有些不一样。在开始的例子中,我们还实现了一个 rooftop_status 函数,来判断下当前的天台状是否人满。但是我们现在是直接返回了函数名,这样函数调用后我们就没办法做任何事情。梅西和德国慌了,我们也慌了,各个都要天台见,但在这之前我们也要考虑下天台的情况。
为了能判断天台的情况,所以此时我们需要在嵌套一层函数,将实现额外功能的部分写在内层函数中,然后将这个内层函数返回即可。这也是为什么装饰器都是嵌套函数的原因。
另外,开篇的例子并没有返回值,也没有参数,要对既有参数又有返回值的函数进行装饰的话,还需要进一步完善。 能够处理返回值的装饰器:
def guess_win(func):
def rooftop_status():
result = func()
print('天台已满,请排队!') return result return rooftop_status@guess_windef german_team():
print('德国必胜!') return '赢了会所嫩模!输了下海干活!'x = german_team()
print(x)
复制代码
输出结果:
德国必胜!
天台已满,请排队!
赢了会所嫩模!输了下海干活!
复制代码
能够处理参数的装饰器:
def guess_win(func):
def rooftop_status(*args, **kwargs):
result = func(*args, **kwargs)
print('天台已满,请排队!') return result return rooftop_status@guess_windef german_team(arg):
print('{}必胜!'.format(arg)) return '赢了会所嫩模!输了下海干活!'x = german_team('德国')
y = german_team('西班牙')
print(x)
复制代码
输出结果:
德国必胜!
天台已满,请排队!
西班牙必胜!
天台已满,请排队!
赢了会所嫩模!输了下海干活!
复制代码
总结
装饰器的本质是函数,其参数是另一个函数(被装饰的函数)。装饰器通常会额外处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。行为良好的装饰器可以重用,以减少代码量。
对于这届的世界杯,我总结了下。
我有一个微信公众号,经常会分享一些python技术相关的干货;如果你喜欢我的分享,可以用微信搜索“python语言学习”关注