Python


523 浏览 5 years

10 装饰器

版权声明: 转载请注明出处 http://www.codingsoho.com/

装饰器

装饰器是一种非嵌入式的修改你代码的行为

本节涉及以下几个内容:

  • 什么是装饰器,其意义是什么?
  • 被装饰的对象
  • 装饰器使用的形式
  • 实现装饰器
  • 装饰器的副作用及如何弥补

什么是装饰器

装饰器是一种非嵌入式修改python对象的定义,改变其行为的工具。
这些对象一般是函数方法,通过改变其代码,在其定义执行或者实例化的时候,执行额外的代码,来修改或者增添新的行为。

装饰器语法:@

问题:如果我们想在一些函数执行的开始和结束打印一些信息,怎么做?

如果是函数:在每一个函数里添加打印 >> 工作量巨大,且不易维护
如果是:在基类里面添加,并且确认需要的继承类里面保证调用基类的。

Python有更简单的方法 : 装饰器 decorator

被decorator的函数,会在函数体之外额外执行一个操作。函数本身不会改变,但实现的功能会更丰富。

@decorator
def function( parameter )
    body

在装饰器函数内,函数会变成一个可调用对象 function will become a callable object inside decorator function

function = decorator (function)

装饰是一种重新绑定行为

代码实例:

def decor_wrap_enter_exit(function):                                         #(1)
    def wrap_enter_exit(param):                                              #(2)
        print "call function enter " + function.__name__                     #(3)
        function(param)                                                      #(4)
        print "call function exit " + getattr(function,'__name__')           #(5)
    return wrap_enter_exit                                                   #(6)

@decor_wrap_enter_exit                                                       #(7)
def hello_world(param):                                                      #(8)
    print "hi "+ param

if __name__ == "__main__":  
    hello_world("hello world!")

解释如下:

  1. 装饰器函数,在需要被装饰的函数前面添加@ decor_wrap_enter_exit, 参数function为被装饰函数
  2. 因为装饰器函数需要返回一个函数,所以需要在装饰器函数内部定义一个函数完成装饰功能,然后该函数作为装饰器函数的返回值
  3. 装饰功能:在函数调用之前先打印enter信息,这儿f.__name__会返回被装饰函数的名字。函数本身也是一个对象,它有一个name属性,返回函数的名字。
  4. 真正的调用函数
  5. 装饰功能:在函数调用结束之后打印exit信息,这儿getattr(f,’__name__’)同样返回被装饰函数的名字
  6. 装饰器返回经过装饰的函数。装饰器是一个闭包函数,返回内部定义的一个函数。
  7. 执行装饰
  8. 函数名hello_word和参数param分别对应于(1)和(2)两个函数的参数

装饰器的特点

  • 装饰器也是一个闭包函数, 闭包函数返回内部定义的一个函数
  • 装饰器接收函数作为参数,是一个高阶函数
  • 装饰器返回一个函数
  • 装饰器置于函数定义的前一行,并前置@符号, @符号表示这是一个装饰器
    • 调用时相当于执行函数=装饰器(函数)语句
    • 函数名被重新赋值了
  • 如果想要__name__返回原来的函数名, 在装饰器中需要返回的函数前调用@functools.wraps(func) (这个后面章节描述)
import functools
def decor_wrap_enter_exit(function): 
    @functools.wraps(func)
    def wrap_enter_exit(param): 
        ......
  • 装饰器函数的参数param形式是(*args, **kwargs), 它接收任意形式的参数 (这个后面章节描述)