看下面一段代码
def fun(a): b = 10 def inner_fun(): print(a + b) return inner_fun x = fun(5) print(type(x)) print(x) x() ''' 输出: <class 'function'> <function fun.<locals>.inner_fun at 0x000001BB0CB02948> 15 '''可以发现在一个函数里定义了一个内部函数,这个内部函数又被外部函数当做值返回了出去。 可以看到用type打印x,结果是<class ‘function’>证明它返回的是一个函数,而在下一段打印中表明了这个函数是fun函数的内部函数还可以看到一片内存地址。 也就是经过上面的操作x这个变量拿到了内部函数inner_fun的内存地址调用x就相当于调用inner_fun,像这种情况的函数以及使用称之为闭包
看下面这个例子,可以发现我把一个函数当做参数传入了fun中。从输出可以看到fun1被调用了,inner_fun也被调用了,可以得到结论,函数被当做参数时,仍可以被内部的函数当函数调用
def fun(f): def inner_fun(): f() print('内部函数的内容') return inner_fun def fun1(): print('fun1的内容') x = fun(fun1) x() ''' 输出: fun1的内容 内部函数的内容 '''再来看下面的代码,可以发现即使没有通过闭包的方式返回内部函数,调用fun1时也执行了inner_fun。此时可以发现代码中多了一段@fun 这段代码的作用就是给下面的函数,即fun1,加上装饰器用的,而上面这个fun函数则就是装饰器
def fun(f): def inner_fun(): f() print('内部函数的内容') return inner_fun @fun def fun1(): print('fun1的内容') fun1() ''' 输出: fun1的内容 内部函数的内容 '''先看下面这个代码,可以发现即使什么也没有调用,fun函数依然被使用了,而可以知道inner_fun并没有被使用
def fun(f): print('装饰器开始执行') def inner_fun(): f() print('内部函数的内容') print('装饰器执行完毕') return inner_fun @fun def fun1(): print('fun1的内容') ''' 输出: 装饰器开始执行 装饰器执行完毕 '''接着看下面的代码,可以发现当打印fun1时返回的函数竟然是inner_fun。于是就可以大胆的做出推测在用@fun时,py就做了一个默认的处理操作也就是使用了fun1 = fun(fun1)从而对原本的fun1进行了修饰。
def fun(f): print('装饰器开始执行') def inner_fun(): f() print('内部函数的内容') print('装饰器执行完毕') return inner_fun @fun def fun1(): print('fun1的内容') print(fun1) ''' 输出: 装饰器开始执行 装饰器执行完毕 <function fun.<locals>.inner_fun at 0x00000128B7F0E0D8> '''于是可以得出结论,在加了装饰器,运行时会事先默认对下面的函数当做参数传入装饰器函数,并用装饰器的返回值代替原函数
当被装饰的函数带参数会如何呢?看看下面的这段代码。 可以发现会报错误,为什么呢? 因为此时的fun1已经不是原来的fun1了,它现在是inner_fun,而inner_fun并没有形式参数,所以发生了错误,要修正也很容易,只需要把inner_fun加上参数就可以了
def fun(f): def inner_fun(): f() print('内部函数的内容') return inner_fun @fun def fun1(x): print('fun1的内容----{}'.format(x)) fun1(5)修正~
def fun(f): def inner_fun(x): f(x)# 这里也要带参数,因为这里的调用的是原本的fun1 print('内部函数的内容') return inner_fun @fun def fun1(x): print('fun1的内容----{}'.format(x)) fun1(5)当参数为多个,甚至有关键字参数时,就可以和普通的函数一样,用可变参数即可
def fun(f): def inner_fun(*args, **kwargs): f(*args, **kwargs)# 这里需要拆包,因为是要当实际参数使用的 print('内部函数的内容') return inner_fun @fun def fun1(x): print('fun1的内容----{}'.format(x)) @fun def fun2(x, y): print('fun2的内容----{}和{}'.format(x, y)) fun1(5) fun2(8, 9) ''' 输出: fun1的内容----5 内部函数的内容 fun2的内容----8和9 内部函数的内容 '''看下面的代码,如果对一个函数使用两个装饰器,会如何呢? 从输出结果可以很明确的看到,当时用多层装饰器时,python会优先加载距离函数最近的那个一装饰器。 也就是先会运行decorate2然后运行decorate1,可以输出查看,最后的函数时decorate1的内部函数
def decorate1(f): print('装饰1载入') def inner_decorate(*args, **kwargs): f(*args, **kwargs) print('执行装饰1') return inner_decorate def decorate2(f): print('装饰2载入') def inner_decorate(*args, **kwargs): f(*args, **kwargs) print('执行装饰2') return inner_decorate @decorate1 @decorate2 def fun1(x): print('fun1的内容----{}'.format(x)) fun1(5) ''' 输出: 装饰2载入 装饰1载入 fun1的内容----5 执行装饰2 执行装饰1 '''有时,装饰器可能要带一个参数,那么带参数的装饰器怎么写呢? 看一下下面的写法: 这是一个错误的写法,因为装饰器中要默认要传的是下面的那个函数,不能传其他参数,但是如果希望在装饰函数时根据一些参数对装饰器进行调整呢?
def decorate1(f): print('装饰1载入') def inner_decorate(*args, **kwargs): f(*args, **kwargs) print('执行装饰1') return inner_decorate @decorate1(5) def fun1(x): print('fun1的内容----{}'.format(x)) print(fun1)那么,套娃大法无限好。 只需要再套一层外部函数,把原本的函数当成返回值返回出去就可以了,可以这么想,在执行@时,需要拿到的是一个函数,而decorate(5)返回的就是函数,那么就相当于@是一个内部的函数,即@的是dec
def decorate(a: int): def dec(f): print('装饰1载入:{}'.format(5)) def inner_decorate(*args, **kwargs): f(*args, **kwargs) print('执行装饰1') return inner_decorate return dec @decorate(5) def fun1(x): print('fun1的内容----{}'.format(x)) fun1(100)