装饰器

参考: 

http://www.wklken.me/posts/2013/07/19/python-translate-decorator.html

http://www.cnblogs.com/wupeiqi/articles/4980620.html

一、装饰器必备知识

1、函数可以被赋值给一个变量

def show(msg):
    print(msg)

# 调用 show 函数
show("这是调用 show() 的输出")

# 将函数 show 赋值给 foo
foo = show

# 调用 foo
foo("这是调用 foo() 的输出")

# 删除 f1 函数后再次调用 f2
del show
foo("这是删除 show() 之后条用 foo() 的输出")

###########输出结果
这是调用 show() 的输出
这是调用 foo() 的输出
这是删除 show() 之后条用 foo() 的输出

2、函数可以被重新定义

def show():
    print("第一次定义 show")

def show():
    print("第二次定义 show")

# 函数的输出会是:第二次定义 show
show()

3、函数的函数体里面也可以再定义函数,并且在函数体里面可以立即被调用

def show():
    def foo():
        return "WenChong"
    print(foo())

4、函数的返回结果也可以是函数

def run(action='remove'):

    def remove_user():
        return "del user name"

    def add_user():
        return "add user name"

    if action == 'remove':
        return remove_user
    else:
        return add_user


# 将 run() 函数的返回值赋值给 action
action = run(action='add')

# 输出 action 的值会是一个函数对象
print(action)

# 在 action 后面加上 () ,会将 add_user()  函数的返回值输出
print(action())

# 也可以直接这样输出
print(run(action='add')())


#######输出结果
<function run.<locals>.add_user at 0x10ce16840>
add user name
add user name

5、函数也可以作为一个参数传递给函数

def show():
    return "WenChong"


def foo(func):
    msg = func()
    print(msg)

# 将函数 show 作为变量传递给 foo 函数
foo(show)


#######输出结果
WenChong

6、动态参数

def my_func(*args,**kwargs):
    print(args,kwargs)

二、写一个装饰器

装饰器是以另一个函数作为参数的函数

def func_action(my_func):

    # 定义个一个 inner_func 函数,用户在 my_func 函数执行前后再执行其他代码
    def inner_func():
        print("my_func start...")
        # 调用 my_func 函数,记得后面一定要有()
        my_func()
        print("my_func end...")
    # 返回 inner_func 函数,这个函数里面包含了 my_func 函数的代码以及 my_func 前后的代码
    return inner_func


# 创建一个函数
def my_func():
    print("my_func 函数被调用")

# 调用 my_func 函数
my_func()

######输出结果
my_func 函数被调用

func_aciton 函数可以传递一个函数作为参数,并在这个参数的前后增加代码

那么就可以将 my_func 函数传递给 func_aciton 以完成对 my_func 的装饰

# 将 func_action 在赋值给一个变量 other_my_func
other_my_func = func_action(my_func)

# 调用 other_my_func 函数
other_my_func()


######输出结果
my_func start...
my_func 函数被调用
my_func end...

这样每次每次想在某个函数的前后增加代码的时候都可以通过 func_aciton 重新赋值给另外一个变量,并执行。

三、使用装饰器

通过上面的例子使用装饰器过于麻烦,需要定义另一个变量,然后再调用这个变量,python 提供了特殊的语法,在函数的上面使用 @func_action 添加装饰器

@func_action
def my_func():
    print("my_func 函数被调用")

# 调用 my_func 函数
my_func()


#####输出结果
my_func start...
my_func 函数被调用
my_func end...

@ 符号的作用:

1、将@符号下面的函数作为参数传递给@符号后面的函数,并执行

2、将返回的结果再重新赋值给@符号下面的函数

四、向装饰器函数传递参数以及获取返回值

当被装饰的函数有参数时,可以通过动态参数的方式来给被装饰的参数传递参数

当被装饰的函数有返回值时,可以通过捕获 func 的返回值通过 inner_func 返回

def func_action(func):
    # 传递参数给别装饰的函数
    def inner_func(*args,**kwargs):
        print("func start...")
        # 获取被装饰函数的返回值
        ret =  func(*args,**kwargs)
        print("func end...")
        # 将被装饰函数的返回值返回给 inner_func
        return ret
    return inner_func

五、向装饰器传递参数

装饰器是一个以函数为参数的函数,那么如果需要向装饰器传递参数时,需要声明一个用于创建装饰的函数

# 声明创建装饰器的函数
def decorator_maker(msg1,msg2):

    print("decorator start make...",msg1)
    # 真正的装饰器
    def my_decorator(func):
        def inner(*args,**kwargs):
            print('func run start...')
            ret = func(*args,**kwargs)
            print('func run end...')
            return ret
        return inner
    print("decorator end make...",msg2)
    return my_decorator

@decorator_maker('Wen','Chong')
def show(msg):
    print(msg)
    return msg

# 调用被装饰过后的函数 
show('aaaa')

输出结果:

decorator start make... Wen
decorator end make... Chong
func run start...
aaaa
func run end...

一个可以处理任何参数的装饰器代码片段

# 装饰 装饰器 的装饰器 (好绕.....)
def decorator_with_args(decorator_to_enhance):
    """ 
    这个函数将作为装饰器使用
    它必须装饰另一个函数
    它将允许任何接收任意数量参数的装饰器
    方便你每次查询如何实现
    """

    # 同样的技巧传递参数
    def decorator_maker(*args, **kwargs):

        # 创建一个只接收函数的装饰器
        # 但是这里保存了从创建者传递过来的的参数
        def decorator_wrapper(func):

            # 我们返回原始装饰器的结果
            # 这是一个普通的函数,返回值是另一个函数
            # 陷阱:装饰器必须有这个特殊的签名,否则不会生效
            return decorator_to_enhance(func, *args, **kwargs)

        return decorator_wrapper

    return decorator_maker
原文地址:https://www.cnblogs.com/wenchong/p/5827717.html