装饰器是什么?

1. 装饰器是什么?

装饰器,就是一个用来装饰的东西;
它长这样子:

@xxx

其中,@是它的语法糖,xxx 是它的名字;
装饰器实际上就是一个函数;
他能装饰函数

2. 装饰器有何用?

在项目中,当你写了很多函数,你想知道运行时都有哪些函数在执行,
而且,你又不想写测试;
那么,你可以这样:

def foo(arg):
    print('%s is running' % foo.__name__)

这样虽然可以,但是你不觉得在每个函数后面加上这一行很麻烦么?
等你加完,测试完后,又得一个个删除,你不觉得‘蓝瘦香菇么’?

不过,你如果了解装饰器,那么,你可以这样写:

def my_log(func):
    def wrapper(*args, **kwargs):
        print(''%s is running' % func.__name__)
    return wrapper
@my_log
def foo(arg):
    print('i`m foo')

@my_log
def_foo1(arg)
    print('i`m foo1')

其中,my_log函数就是装饰器;
这样你就可以把@my_log加在需要装饰的函数前面;
不需要的时候再全局搜索删除即可;
如此,方便又快捷;

3.装饰器原理

装饰器实质上就是一个函数
要理解装饰器原理,我们可以回到刚刚那个问题;
假装你还不知道装饰器;
你再三思考;
想到了这样的一个方法:

def foo():
    pass
def foo1():
    pass
def foo2():
    pass

def print_log():
    print ('foo is running')
    foo()
    print ('foo1 is running')
    foo1()
    print ('foo2 is running')
    foo2()

这样看起来还不错,避免修改原来的函数;
但是,每个函数都得写一次print;
感觉太麻烦;
于是,你优化了一下代码:

def print_log(func):
    print('%s is running' % func.__name__)
    func()
print_log(foo)
print_log(foo1)
print_log(foo2)

这样省去很多个print,看起来好多了;
但是,当你想到出现这种情况的时候:

def main():
    print_log(foo)
    a = print_log(foo1)
    b = print_log(foo2) + m
    for i in print_log(foo):
        i = i + n

看到满屏幕的print_log,感觉好恐怖;
于是,又想到一个办法:

def print_log(func):
    print('%s is running' % func.__name__)
    return func
foo = print_log(foo)
foo1 = print_log(foo1)
foo2 = print_log(foo2)

foo()
foo1()
foo2()

这样用回了原来的函数名,舒服,一看就懂;
但是,如果这些函数是带参数的,要怎么办呢?
显然,这方法对带参数的函数是不行的;
你想着这样写:

foo = print_log(foo(arg))

但是,细想又不对,foo本应该是作为参数对象,加上括号后foo就直接被执行了;
foo需要做的只是静静的呆着,而不是执行起来,这,不是你想要的;
一顿冥思苦想之后,你想起了返回函数;它,可以接收参数!

def print_log(func):
    def wrapper(*arg, **kwargs):
        print('%s is running' % func.__name__)
        return func(*arg, **kwargs)
    return wrapper

foo = print_log(foo)

foo(1)

这样,foo = print_log(foo)就相当于把foo指向了wrapper
wrapper可以有参数,那么,foo也可以有参数了;
这样,需求实现,完美~
但是,foo = print_log(foo)可不可以自动完成呢?
可以,定个规则,@print_log,取名装饰器;
呼呼~突出一口浊气;
人生苦短,来学python;
以为这样就是结局了么?
装饰器如果也需要参数,那怎么办?
emmm....
那就加多一层呗:

def log(args):
    def print_log(func):
        def wrapper(*arg, **kwargs)
            print('%s is running' % func.__name__)
            print(args
            return func(*arg, **kwargs)
        return wrapper
    return log

到此,装饰器就差不多了;
等等,你运行代码后又发现,执行结果不对呀;
func.__name__变成了wrapper了;
这时候,只需要在wrapper前面加上
wrapper.__name__ = func.__name__即可;

def log(args):
    def print_log(func):
        def wrapper(*arg, **kwargs)
            wrapper.__name__ = func.__name__
            print('%s is running' % func.__name__)
            print(args
            return func(*arg, **kwargs)
        return wrapper
    return log

Python内置的functools.wraps,就可以实现wrapper.__name__ = func.__name__;
只需要在wrapper函数前面加上 @functools.wraps(func)即可;

import functools
def log(args):
    def print_log(func):
        @functools.wraps(func)
        def wrapper(*arg, **kwargs)
            print('%s is running' % func.__name__)
            print(args
            return func(*arg, **kwargs)
        return wrapper
    return log

Larwas
请先登录后发表评论
  • latest comments
  • 总共0条评论