在加强原函数的功能的同时,不修改原函数的定义,这种在代码运行期间动态增长功能的方式,就称为装饰器(Decorator)。装饰器,本质是一个返回函数的高阶函数。在了解装饰器以前,也简单介绍下返回函数的相关内容。python
以前,咱们讲太高阶函数,map()
,filter()
函数等高阶函数,可以接受函数做为参数,而函数一样也能够做为结果值返回。微信
先定义一个简单的函数:闭包
>>> def func(): ... print("return function")
上述代码只是普通的一个函数。如果,不须要当即输出,能够返回函数,而不是直接输出:app
>>> 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
这是返回函数的简单应用。code
闭包,是指在一个函数内部定义了另外一个函数,而内部函数引用了外部函数的参数和局部变量,当返回内部函数时,相关参数和变量存储在返回的函数中。orm
下面代码实现一个闭包的操做:get
>>> def lazy_sum(*args): ... def sum(): ... num = 0 ... for x in args: ... num += x ... return num ... return sum ...
这个例子中,内部函数调用了外部函数的参数 args
,当调用 lazy_sum
时,返回的函数存储着相关参数和变量。只有当再次调用返回函数,才会得出运算结果。io
>>> 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()
函数内,首先先打印日志,而后再调用原始函数。
这些就是装饰器的一些内容,至于更深刻的部分,后续会继续更新介绍。
以上就是本篇的主要内容
题外话: 近期网上散布不少的谣言,致使不少人没法正确识别真假。下面的连接,是腾讯新闻的一个平台【较真】,实时给你们辟谣以及科普,能够帮助你们分清真相。尽可能作到不传谣不信谣。
欢迎关注微信公众号《书所集录》