闲逸笔记

keep it simple & stupid

Python 装饰器 Decorator 工作原理分析

1). Decorator 函数的定义

  • 定义一个 demo_decorator,以及一个被它装饰的函数 demo_function。

    def demo_decorator(*args, **kwargs):
        print ">>>> ", str(demo_decorator), " == start ==", args, kwargs
    
        def handle_func(func):
            print "===> ", str(handle_func), str(func)
    
            def handle_args(*args, **kwargs):
                print "---> ", str(handle_args), " -- start --"
                print "---> ", str(handle_args), str(func)
                func(*args, **kwargs)
                print "---> ", str(handle_args), " --  end  --"
    
            print "===> ", str(handle_func), str(handle_args)
            return handle_args
    
        print ">>>> ", str(demo_decorator), str(handle_func)
        print ">>>> ", str(demo_decorator), " ==  end  ==", args, kwargs
        return handle_func
    
    @demo_decorator("decorator arg 1", "decorator arg 2")
    def demo_function(*args, **kwargs):
        print "+++> ", str(demo_function), args, kwargs
    
  • 运行这段代码,结果如下:

    >>>>  <function demo_decorator at 0x01AB51F0>  == start == ('decorator arg 1', 'decorator arg 2') {}
    >>>>  <function demo_decorator at 0x01AB51F0> <function handle_func at 0x01AD3EF0>
    >>>>  <function demo_decorator at 0x01AB51F0>  ==  end  == ('decorator arg 1', 'decorator arg 2') {}
    ===>  <function handle_func at 0x01AD3EF0> <function demo_function at 0x01AD3F70>
    ===>  <function handle_func at 0x01AD3EF0> <function handle_args at 0x01AD3EB0>
    
  • 可以看出:

    • 在执行 @demo_decorator...... 定义时,demo_decorator 中除 hangle_args 内部外,其余代码都会被执行。

    • 函数 demo_function 的入口地址被改为 handle_args(at 0x01AD3EB0)

    • 函数 demo_function 的代码段的地址则以参数 func 方式放到内部 (func -> demo_function at 0x01AD3F70)

    • @demo_decorator 先处理函数 demo_function 的代码段入口(第一个 return:return handle_func),完成后再处理(如果只有第一个 return 则不处理)其参数部分。

2). 引用 Decorator 装饰的函数

  • 引用执行 demo_function

    demo_function("function arg 1", "function arg 2")
    
  • 执行结果:

    --->  <function handle_args at 0x01AD3EB0>  -- start --
    --->  <function handle_args at 0x01AD3EB0> <function demo_function at 0x01AD3F70>
    +++>  <function handle_args at 0x01AD3EB0> ('function arg 1', 'function arg 2') {}
    --->  <function handle_args at 0x01AD3EB0>  --  end  --
    
  • 结论

    • 两个参数 ("function arg 1", "function arg 2") 被传递到 handle_args (at 0x01AD3EB0) 位置。

    • 原函数代码段执行入口 demo_function (at 0x0aAD3F70) 被包裹在 handle_args 内部。

    • 由此看出,包裹函数的代码最好只放在最里面 return 的 handle_args 内部。放到其他位置则会在定义的时候就被执行。

3). 直接使用装饰函数

  • 不使用 @decorator 的格式,而是直接层层调用

    def demo_function2(*args, **kwargs):
        print "+++> ", str(demo_function2), args, kwargs
    
    demo_decorator("decorator arg 1", "decorator arg 2")(demo_function2)("function arg 1", "function arg 2")
    
  • 运行结果

    >>>>  <function demo_decorator at 0x01AB51F0>  == start == ('decorator arg 1', 'decorator arg 2') {}
    >>>>  <function demo_decorator at 0x01AB51F0> <function handle_func at 0x01AD38B0>
    >>>>  <function demo_decorator at 0x01AB51F0>  ==  end  == ('decorator arg 1', 'decorator arg 2') {}
    ===>  <function handle_func at 0x01AD38B0> <function demo_function2 at 0x01AD3F30>
    ===>  <function handle_func at 0x01AD38B0> <function handle_args at 0x01AD3FB0>
    --->  <function handle_args at 0x01AD3FB0>  -- start --
    --->  <function handle_args at 0x01AD3FB0> <function demo_function2 at 0x01AD3F30>
    +++>  <function demo_function2 at 0x01AD3F30> ('function arg 1', 'function arg 2') {}
    --->  <function handle_args at 0x01AD3FB0>  --  end  --
    
  • 结论

    • 效果与前面一致。

    • 相当于首先传递 demo_function 入口地址,再传递 ("function arg 1", "function arg 2") 参数。demo_decorator 则依次进行处理。

4). 其他


Permalink [http://sleepycat.org/blog/36]


发表评论

top bottom