理解带参数的装饰器
参考文章 Python精进-装饰器与函数对象
加了装饰器这段代码从一个函数定义(不会执行任何东西,在内存单元中保存一段代码)变成了一条赋值语句(修改被装饰函数所在内存单元中的内容)exit
- 从运维角度看是装饰器补充了函数的功能, 从开发角度看是装饰器将函数作为参数
- 函数返回的函数无法被直接调用,除非将其返回值赋值个一个变量;列表就相当于这个函数,他的元素必须通过带参数执行这个函数获得。
- 该用函数还是装饰器,可以看是谁调用谁,从参数传递的方向也可以判断。
第一步: 定义装饰函数
def foo(a):
def decorator(func):
def wrap(*args):
if not callable(a):
print(a)
return func(*args)
return wrap
if callable(a):
return decorator(a)
return decorator
# 装饰器带参数可以使得被装饰函数的改变有两种可能
第二步:使用装饰器装饰函数,调用新函数
第一种可能用法
@foo('hello')
def bar(c):
print(c)
return 0
装饰器的处理过程
装饰的作用是获得一个新函数
- bar = foo('hello')(bar)
- 先执行 foo('hello') 得到 decorator 函数
- 然后让decorator 函数带着(bar) 一起飞
- 就变成了 bar = decorator(bar)
- decorator(bar) 执行得到一个函数 wrap(名字不重要,为了得到内存中的地址而已)
- 这个新函数可以有多个参数(因为原函数带参数,装饰后也要带参数啊)
- 额外多说一句,函数里定义的函数是可变的(内存块里存的代码是可变的)
- wrap 怎么写呢?根据外面的decorator函数的参数变着来:
重点来了,开始改写bar: - 因为a='hello' not callable 所以有一行 print('hello') 的代码
- 代码很少,接下来就 return了,return 的是被装饰函数的执行结果。
- 为理解为wrap函数定义为这个时候去被装饰函数定义的内存块执行,并将被装饰函数的返回值返回出去。不知道这样说是否准确???
- 相当于bar函数的函数体(内存中的块内容)变成了:
def bar(c):
print(a)
# 执行被装饰函数体
print(c)
# 将被装饰函数的返回值作为新函数的返回值,还是0
return 0
调用被装饰后的函数显然应该为:
>>> bar('wold')
hello
world
0
第二种用法
@foo
def bar(c):
print(c)
return 0
装饰的作用是获得一个新函数
- foo 函数是带参数的,所以把 bar作为参数执行 foo 函数 得到:
- bar = foo(bar)
- 仔细看foo的函数,如果参数是 callable 则返回 decorator 函数并且带上这个 foo 的参数,所以:
- foo(bar) 变成了 decorator(bar) bar = decorator(bar)
- 先执行 foo('hello') 得到 decorator
- 于是 bar = decorator(bar)
- decorator(bar) 执行得到的是一个 wrap 函数,说白了它的名字不重要,重要的是它里面的内容:
- 里面的内容就这样来写 因为 a 是callable的,所以就不执行
print(a)
了,仅仅执行 func(*args),也就是将新函数的参数,被装饰函数返回它的返回值 - 装饰器啥也没干,直接把被装饰器的代码拿去充数了。
- 相当于bar函数的函数体(内存中的块内容)变成了:
def bar(c):
# 执行被装饰函数
print(c)
# 将被装饰函数的返回值作为新函数的返回值,还是0
return 0
调用被装饰后的函数应该为:
>>> bar('wold')
world
0
思考
定义一个函数其实是分配一个内存空间来装代码。
decorator() 函数执行的过程就是干这个事情:
创建一个 wrap 函数,最好把函数对象的引用丢出去。
这个函数干什么嗯
它先把print('hello') 这段代码记录下来。
再看decorator 的参数 func 是哪个,去他的代码块执行它,然后把返回值返回。