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() 函数内,首先先打印日志,然后再调用原始函数。

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

以上就是本篇的主要内容

题外话:近期网上散布很多的谣言,导致很多人无法正确识别真假。下面的链接,是腾讯新闻的一个平台【较真】,实时给大家辟谣以及科普,可以帮助大家分清真相。尽量做到不传谣不信谣。

https://vp.fact.qq.com/home

欢迎关注微信公众号《书所集录》

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章