为了账号安全,请及时绑定邮箱和手机立即绑定

Python 装饰器

标签:
Python

装饰器


在增强原函数的功能的同时,不修改原函数的定义,这种在代码运行期间动态增加功能的方式,就称为装饰器(Decorator)。装饰器,本质是一个返回函数的高阶函数。在了解装饰器之前,也简单介绍下返回函数的相关内容。

返回函数


之前,我们讲过高阶函数,map()filter() 函数等高阶函数,能够接受函数作为参数,而函数同样也可以作为结果值返回。

先定义一个简单的函数:

>>> def func():    
...     print("return function")

上述代码只是普通的一个函数。若是,不需要立即输出,可以返回函数,而不是直接输出:

>>> def return_func():
...     def func():
...         print("return function")
...     return func  

当调用 return_func() 时,返回的不是输出结果,而是函数:

>>> f = return_func() 
>>> f
<function return_func.<locals>.func at 0x000002C03AF091E0>

当调用 f 时,才输出内容:

>>> f()
return function

这是返回函数的简单应用。

闭包


闭包,是指在一个函数内部定义了另一个函数,而内部函数引用了外部函数的参数和局部变量,当返回内部函数时,相关参数和变量存储在返回的函数中。

下面代码实现一个闭包的操作:

>>> def lazy_sum(*args):
...     def sum():
...         num = 0 
...         for x in args:
...             num += x 
...         return num
...     return sum
... 

这个例子中,内部函数调用了外部函数的参数 args,当调用 lazy_sum 时,返回的函数存储着相关参数和变量。只有当再次调用返回函数,才会得出运算结果。

>>> f = lazy_sum(1,2,3,4,5)
>>> f
<function lazy_sum.<locals>.sum at 0x000002C03AF31488>
>>> f()
15

这里需要注意,每次调用外部函数,返回的函数都是新的函数,即使传入的参数都相同:

>>> f1 = lazy_sum(1,2,3,4,5) 
>>> f2 = lazy_sum(1,2,3,4,5) 
>>> f1 is f2
False
>>> f1 == f2 
False

该例子中,f1()f2() 的结果互不影响。

还有个需要注意的地方,返回函数不是立刻执行,而是调用了 f() 才执行。尝试用另外一个例子说明这种情况,示例如下:

>>> def count():
...     lst = []
...     for i in range(1, 4):
...         def func():
...             return i * i
...         lst.append(func)
...     return func
...
>>> f1, f2, f3 = count()

在这个例子中,每次循环,都创建一个新的函数,将创建的 3 个函数返回。

这里的结果,可能会猜测调用 f1(), f2(), f3() 结果分别是 1, 4, 9,但实际结果却都是 9

>>> f1()
9
>>> f2()
9
>>> f3()
9

这里是因为返回的函数引用了变量 i,但是没有立刻执行。等 3 个函数都返回时,引用的变量 i 的值已经全部变成了 3,所以最终结果是 9

所以,返回闭包时,返回函数不要引用循环变量,或者后续会发生变化的值。

装饰器


前面已经说明,装饰器,本质上是一个返回函数的高阶函数。尝试用例子说明,

>>> def log(func):                      
...     def warpper(*args, **kw):       
...         print('call {}():'.format(func.__name__)
...         return func(*args, **kw)
...     return wrapper

上面的 log 是一个装饰器,接受函数作为参数,返回函数。借助 Python 的 @ 语法,把装饰器置于函数的定义处:

@log
def func():
    print("function name")

调用 func() 函数,会运行 func() 本身函数,还会在运行 func() 前,打印一行日志:

>>> func()
call func():
function name

在这里,将 @log 放到 func() 函数的定义处,相当于下列语句:

func = log(func)

由于 log() 是装饰器,返回一个函数。但是,原来的 func() 还存在,只是现在同名的 func() 指向了新的函数,所以调用的 func() 时,执行的将是 wrapper() 函数。在 wrapper() 函数内,首先先打印日志,然后再调用原始函数。

这些就是装饰器的一些内容,至于更深入的部分,后续会继续更新介绍。


以上就是本篇的主要内容


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消