[TimLinux] Python 再谈装饰器

参考链接:https://stackoverflow.com/questions/739654/how-to-make-a-chain-of-function-decorators

1. 函数对象

  • 能够赋值给其他变量
  • 能够在另外函数内定义
  • 能够作为参数进行传递
  • 能够作为函数的返回值
def firstLevel(f_arg):  # 作为参数,可以赋值
    def tmp_func(*args, **kwargs):   # 可以在另外函数内定义
        # do something ...
        ret = f_arg(*args, **kwargs)
        # do something ...
        return ret
    return tmp_func   # 作为函数的返回值

# 1. 原始方法
def func(*args, **kwargs):
    print("In func")
func = firstLevel(func)

# 2. 装饰器方法
@firstLevel
def func(*args, **kwargs):
    print("In func")

# 使用
func()

2. 多层装饰器

可以对函数进行一层又一层的包装,使用多层装饰器即可

def firstLevel(f_arg):
    def tmp_func(*args, **kwargs):
        print("----s.firstLevel----")
        ret = f_arg(*args, **kwargs)
        print("----e.firstLevel----")
        return ret
    return tmp_func

def secondLevel(f_arg):
    def tmp_func(*args, **kwargs):
        print("----s.secondLevel----")
        ret = f_arg(*args, **kwargs)
        print("----e.secondLevel----")
        return ret
    return tmp_func

#1. 原始方法
def func(*args, **kwargs):
    print("In func")
func = firstLevel(secondLevel(func))
func()

# 2. 装饰器方法
@firstLevel
@secondLevel 
def func(*args, **kwargs):
    print("In func")
func()

@firstLevel 语法,

这个后面看似隐藏了一对(),用来将下面的代码作为装饰器函数返回的内部函数的参数进行传递,当明确指定()的时候,则是给装饰器函数传递参数,

3. 装饰器接收参数

@firstLevel 语法,告诉我们这个函数接收的参数是一个函数对象,内部返回的是一个函数(firstLevel就是函数名,使用@标记告诉我们的)

@firstLevel(arg1, arg2, ...) 语法,告诉我们这个firstLevel(arg1, arg2, ...) 函数接收参数,返回的也是一个函数,返回的函数比如叫 retLevel,则将转化为: @retLevel 语法,retLevel 语法就跟上面的语法一样,告诉我们返回的 retLevel 接收的参数是一个函数对象,内部同样返回了一个函数 (retLevel 就是中间函数名,使用@标记告诉我们的)

def firstLevel(arg1, arg2):
    # do something ...
    print(arg1, arg2)
    # 使用参数做一些事情(这是函数传递参数的目的)
    def tmpRetLevel(f_arg):
        def tmp_func(*args, **kwargs):
            # do something ...
            ret = f_arg(*args, **kwargs)
            # do something ...
            return ret
        return tmp_func

    return tmpRetLevel

# 1. 原始方法
def func(*args, **kwargs):
    print("In func")

retLevel = firstLevel('1', '2')
func = retLevel(func)
func('3', '4')

# 2. 装饰器方法
@firstLevel('1', '2')
def func(*args, **kwargs):
    print("In func")

func('3', '4')

 

4. functools实现装饰器

上面的返回后的函数,打印一些内部变量比如:func.__doc__, __name__ 将变为 内部函数的 __doc__,__name__ 描述信息,functools.wraps将解决这样的问题,wraps()函数本身就是一个装饰器。

import functools


def firstLevel(f_arg):
    @functools.wraps(f_arg):
    def tmpFunc(*args, **kwargs):
        # do something ...
        ret = f_arg(*args, **kwargs)
        # do something ...
        return ret
    return tmpFunc

@firstLevel
def func(*args, **kwargs):
    print("In func")

print(func.__name__)  # 输出:func

4. 使用场景

  • 装饰外部提供的库
  • 避免大量重复代码的编写
  • 比如:Django对视图的权限控制,Twisted将函数修改为异步调用

 

posted @ 2018-09-26 09:33  TimLinux  阅读(158)  评论(0编辑  收藏  举报