Python中的装饰器

当你在处理大批量的数据时,你想测试一下你写的代码要运行多久,如果你用的是anaconda,那没问题,可以直接%%time来计算此单元格的运行时间,呢么在Pycharm上要怎么做呢,你可以导入time模块去计算一个时间差,但是如果多段代码都需要分别计算执行时间,需要在每个函数上都写一边计时代码吗? 这样岂不是很麻烦,这时我们可以利用装饰器来实现,不仅可以减少重复代码,而且可以在不改变原来代码的基础上增加新的功能。

1.闭包函数

在说装饰器之前,先说一下闭包函数。Python是一种面向对象的编程语言,在Python中一切皆为对象,所以变量所拥有的属性,函数同样可以拥有,所以,在函数内部再创建一个函数是完全合理的。这种函数称为内嵌函数。这种函数只可以在外部函数的作用域内被正常调用,在外部函数的作用域之外调用会报错,例如:

Python中的装饰器_第1张图片

而如果内部函数里引用了外部函数里定义的对象,那么此时内部函数就被称为闭包函数。

举一个闭包的例子:

def test():
    a = 1
    def sum():
        b = 2
        return a + b  
    return sum

内部函数调用了自由变量a,外部函数返回内部函数,形成闭包。

2. Python装饰器

Python中的装饰器本身也是一个函数,它可以实现在不改变原函数的基础上增加新的功能,装饰器的返回值也是一个函数对象。装饰器函数的外部函数传入我要装饰的函数名字,返回经过修饰后函数的名字;内层函数(闭包)负责修饰被修饰函数。

例如为原函数增加新的功能:

def decorator(func):
    def inner():
        print('new function1')
        func()
        print('new function2')

    return inner

@decorator
def test():
    print('old function')


if __name__ == '__main__':
    test()

运行结果如下:

Python中的装饰器_第2张图片

@decorator这个语法相当于 执行 func = decorator(func),为func函数装饰并返回。

如果原函数需要传参怎么办?此时我们需要修改装饰器代码,用到Python中的*args与**kwargs*args表示的参数以列表的形式传入,**kwargs表示的参数以字典的形式传入举个例子:

def test(*args, **kwargs):
    print(f'args:{args}')
    print(f'kwargs:{kwargs}')


test(1, 2, 3, [1, 2, 3], a=1, b=2)

结果如下:(不懂的可以百度- . -)

原函数带参数,装饰器该怎么写?

def decorator(func):
    def inner(*args, **kwargs):
        print('new function1')
        res = func(*args, **kwargs)
        print('new function2')
        return res

    return inner


@decorator
def test(x):
    print('old function')
    return f'传入的参数x为:{x}'


if __name__ == '__main__':
    res = test(3)
    print(res)

原函数返回了某个值,因此在内增函数执行func时,同样需要将这个值返回。可以调整需要装饰的函数在内层函数的位置,即可以实现新增工能在需要装饰的函数之前执行还是之后执行。运行效果如下:

Python中的装饰器_第3张图片

 

你以为到这一步就算完成了吗? 当然不是~   所以请看下面代码:(只是修改了main下面的代码)

def decorator(func):
    def inner(*args, **kwargs):
        print('new function1')
        res = func(*args, **kwargs)
        print('new function2')
        return res

    return inner


@decorator
def test(x):
    print('old function')
    return f'传入的参数x为:{x}'


if __name__ == '__main__':
    print(test.__name__)

运行效果如下:

为什么函数的__name__变成了inner而不是test呢,因为被装饰函数的信息在此过程中丢失了,需要借助functools来保持原有信息。

from functools import wraps


def decorator(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print('new function1')
        res = func(*args, **kwargs)
        print('new function2')
        return res

    return inner


@decorator
def test(x):
    print('old function')
    return f'传入的参数x为:{x}'


if __name__ == '__main__':
    print(test.__name__)

运行结果如下:

你可能感兴趣的:(Python,Python,装饰器)