装饰器

装饰器decorator 是我之前一直很难懂的地方,曾经想死记装饰器的写法,但不得要领。现在,自己又实现了一次装饰器的编写,发现并没有那么难懂呢。发现,其实,它跟js的闭包非常的像。甚至说一致,然后再加上一个f=myfnc(f)的作用域替换。

所以,装饰器模式,首先能闭包,即函数内能继续嵌套定义函数,内部的函数,享受外部作用域的变量值。另外,有一种方便的方式,覆盖原函数。

关于理解:从调用的方式来看,最外层的函数,最先调用。最内部的函数,是最后调用,而且是跟原函数一样的参数结构。每调用一次,脱一层。所以,理论上讲,能嵌套很多层,视具体情况而定。从最终调用的函数,即定义在最内部的函数来看,它可能需要很多参数,在本层解决不了,那么,没有关系,我们可以在最外边加一层,一层不行,加二层,反正其他层的变量,在此都是生效的,都是为了本层服务的。(其实,这也是自己理解js的闭包层层封装,js是方便,生成各种的调用方法)(js理论上讲也容易实现装饰器方式,只是没有@那种语法)。

正文

带参数和不带参数

以下实现,带参、不带参均可调用的装饰器,高明之处,是对调用的参数进行判断。(当然也可以不准)

def log(text=None):
    if isinstance(text, str):
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kw):
                print('%s %s():' % (text, func.__name__))
                return func(*args, **kw)
            return wrapper
        return decorator
    else:
        @functools.wraps(text)
        def wrapper(*args, **kw):
            print('call %s():' % text.__name__)
             return text(*args, **kw)
        return wrapper

示例1

代码中需要将数据插入到数据库中,但是下面的函数结构基本上都是固定的,这个时候,改为装饰器的方式进行封装,减少无用的代码使用。参考来源:python教程

def save(table_name,primary_key='projectname'):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            row = func(*args, **kw)
            data,db,path = args
            try:
                item = db.one(table_name,'projectname','where '+ primary_key + ' = %s',[data.projectname])
                if item:
                    projectname = item[0]
                    print('更新 ' + table_name)
                    db.update(table_name,row,'projectname = %s',[projectname])
                else:
                    print('插入 ' + table_name)
                    db.insert(table_name,row)
            except Exception as e:
                print('插入或更新失败:%s  %s'%(data.projectname,e))
        return wrapper
    return decorator

调用时:

效果,直接将返回的数据,插入到数据库的表中。

@save('git_summary')
def save_sumarry(data,db,path):
    return {
        'projectname':data.projectname,
        'first_commit':data.getFirstCommitDate().strftime(format),
    }

warp的参数,也可以如实写成跟原函数一样的形式,

def wrapper(data,db,path ):
            row = func(data,db,path )