# 装饰器的语法糖本质
# 装饰器的语法糖本质
有装饰器被装饰函数如下:
def log_call(func):
def proxy(*args, **kwargs):
logging.info('begin call: {name}'.format(name=func.__name__))
result = func(*args, **kwargs)
logging.info('call done: {name}'.format(name=func.__name__))
return result
return proxy
@log_call
def work_bar(data):
pass
对其进行字节码编译可获得如下结果:
2 0 LOAD_NAME 0 (log_call)
2 LOAD_CONST 0 (<code object work_bar at 0x100cf2d20, file "<dis>", line 2>)
4 LOAD_CONST 1 ('work_bar')
6 MAKE_FUNCTION 0
8 CALL_FUNCTION 1
10 STORE_NAME 1 (work_bar)
12 LOAD_CONST 2 (None)
14 RETURN_VALUE
注意第8条字节码-CALL_FUNCTION 实际就是把246生成的函数作为参数传递给log_call函数调用。
可以发现实质上@log_call 这行代码的作用只是告诉 Python 编译器,在函数定义后面插入代码 work_bar = log_call(work_bar)。
- 第一条字节码将 log_call 函数加载进当前执行栈栈顶;
- 第二、三条字节码将 work_bar 代码对象和 work_bar 函数名加载到栈顶,为创建 work_bar 函数做好准备;
- 第四条字节码完成 work_bar 函数创建,该字节码执行完毕后,work_bar 函数便位于栈顶;
- 第五条字节码则以 work_bar 为参数调用 log_call 函数,并将 log_call 返回的 proxy 函数保存于栈顶;
- 接下来的 STORE_NAME 从栈顶取出 proxy 函数并保存到当前局部名字空间,它一般也是模块的属性空间;