Python基础装饰器

作者:Zarten
知乎专栏:Python基础深入详解
知乎ID: Zarten
简介: 互联网一线工作者,尊重原创并欢迎评论留言指出不足之处,也希望多些关注和点赞是给作者最好的鼓励 !

概述

装饰器其实就是一个函数,返回值是一个函数对象,它可以让其他函数在不需要做任何代码改动的前提下增加额外功能,比如:日志功能、性能测试、缓存、事务处理、权限校验等。

为了理解装饰器,我们要先理解一个概念,即:python里面一切皆对象,包括函数。

函数装饰器详解

最简单的装饰器

def my_decorator(f):
    return f

这个装饰器什么也没做,返回原函数对象。

装饰器的使用

def my_decorator(f):
    return f

@my_decorator
def fun():
    print('zarten')

fun()

上面代码等价于:

def my_decorator(f):
    return f

def fun():
    print('zarten')

fun = my_decorator(fun)
fun()

一般使用第一种方式,python中特有的装饰器语法,更加简洁。

装饰器返回另外一个函数

装饰器通常都是返回另外一个函数,而不是原函数本身

def my_decorator(f):
    def other_fun():
        print('other_fun')
    return other_fun

@my_decorator
def fun():
    print('zarten')

fun()
print(fun)

调用fun()函数,其实是运行装饰器里面的other_fun()函数。从输出结果也可看到,fun()函数现在是other_fun()函数的引用。

从输出结果可以看到,并没有执行fun()函数内的代码,这是因为在装饰器内部执行了other_fun()函数,但并没有在内部执行fun()函数,所以一般在装饰器传入参数,这个参数是被装饰的函数对象,都会在装饰器内部调用并返回其结果

def my_decorator(f):
    def other_fun():
        print('other_fun')
        return f()
    return other_fun

@my_decorator
def fun():
    print('zarten')

fun()
print(fun)

functools.wraps的作用

我们知道,执行fun()函数,实际上是执行了装饰器里面的other_fun()函数,并在内部返回了原函数。但这样有个缺点是:原函数的一些元信息发生了改变,如docstring、name、参数列表等。如下代码所示:

def my_decorator(f):
    def other_fun():
        print('other_fun')
        return f()
    return other_fun

@my_decorator
def fun():
    print('zarten')

print('fun name:', fun.__name__)

为了防止元信息改变,python引入了functools.wraps,用法如下

from functools import wraps
def my_decorator(f):
    @wraps(f)
    def other_fun():
        print('other_fun')
        return f()
    return other_fun

@my_decorator
def fun():
    print('zarten')

print('fun name:', fun.__name__)

一个完整简单的日志打印装饰器

from functools import wraps

def my_decorator(f):
    @wraps(f)
    def other_fun(*args, **kwargs):
        print('%s in running' % f.__name__)
        return f(*args, **kwargs)
    return other_fun

@my_decorator
def fun():
    print('zarten')

fun()

带参数的装饰器

装饰器是把被装饰函数作为第一个参数传进去,若装饰器带有参数时,怎么接收其他额外的参数呢?

方法是:将额外的参数作为第一个参数传进去,然后返回一个函数对象,这个函数的参数才是被装饰函数的对象

from functools import wraps

def my_decorator(zhihu_id= 'zarten'):
    def decorator(f):
        @wraps(f)
        def other_fun(*args, **kwargs):
            print('%s in running.name is %s' % (f.__name__, zhihu_id))
            return f(*args, **kwargs)

        return other_fun
    return decorator

@my_decorator('zarten_2')
def fun():
    print('zarten')

fun()

从上面代码可以看到,装饰器使用的默认参数,若装饰器不传递任何参数时,也需要使用空括号,如@my_decorator()

类装饰器详解

类装饰器,也就是说装饰器是一个类了,而不是一个函数了。类装饰器和函数装饰器非常类似。

可以利用类的特殊方法call()来实现,实现此特殊方法后,类对象可以像函数一样调用。PS:若对python中的常用特殊方法不了解的,可以查看我之前写的文章,点这里

类装饰器的实现

class Decorator():
    def __init__(self, f):
        self.f = f

    def __call__(self, *args, **kwargs):
        print('%s in running' % self.f.__name__)
        self.f(*args, **kwargs)

使用类装饰器

使用类装饰器跟使用函数装饰也是非常的类似

@Decorator
def fun():
    print('zarten')

fun()

此时被装饰的函数对象作为装饰器类的参数传入,返回一个类装饰器的对象,由于装饰器类里实现了call()的特殊方法,固可以直接调用这个对象。

若不使用@Decorator语法糖,上面代码等价于:

def fun():
    print('zarten')

fun = Decorator(fun)
fun()

完整代码如下:

class Decorator():
    def __init__(self, f):
        self.f = f

    def __call__(self, *args, **kwargs):
        print('%s in running' % self.f.__name__)
        self.f(*args, **kwargs)

@Decorator
def fun():
    print('zarten')

fun()



✄------------------------------------------------

原文地址:https://zhuanlan.zhihu.com/p/72798053

转载请标明来之:阿猫学编程
更多教程:阿猫学编程-python基础教程

所有评论

如果对文章有异议,请加qq:1752338621