我是靠谱客的博主 无私胡萝卜,最近开发中收集的这篇文章主要介绍python中装饰器的使用教程详解(wraps),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

先看下面的函数简单理解一下

示例代码:

import logging


def use_logging(func):
    logging.error("%s is running" % func.__name__)
    func()


def bar():
    print('ccc')


use_logging(bar)

运行结果:

        上述代码中逻辑上不难理解,但每次都要将一个函数作为参数传递给use_logging函数。而且这种方式已经破坏了原有的代码逻辑结构,之前执行业务逻辑时,执行运行bar(),但是现在不得不改成use_logging(bar)。有种简单的方法就是使用装饰器。

简单装饰器实现:

示例代码:

import logging


def use_logging(func):
    def wrapper(*args, **kwargs):
        logging.error("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper


def bar():
    print('ccc')


bar = use_logging(bar)
bar()

运行结果:

        函数use_logging就是装饰器,它把真正执行的方法放在了func函数里面,看起来bar被use_logging装饰了。

@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作。

示例代码:

import logging


def use_logging(func):
    def wrapper(*args, **kwargs):
        logging.error("%s is running" % func.__name__)
        return func(*args, **kwargs)

    return wrapper


@use_logging
def bar():
    print('aaa')


@use_logging
def foo():
    print('bbb')


bar()
foo()

运行结果:

         如上述代码所示,可以省去bar = use_logging(bar)这一句定义了,直接调用bar()即可得到想要的结果。对于其它函数也是可以直接调用装饰器来修饰函数的,而不用再次重复性的修改函数或增加新的封装。这样就提高了程序的可重复利用性,并增加了程序的可读性。

带参数的装饰器:

        装饰器在使用的过程中可以传递参数,增大了装饰器的灵活性。在上述代码中,@use_logging装饰器唯一的参数就是执行业务的函数,在使用装饰器的时候可以传递参数,比如@decorator(a),这就为装饰器的编写和使用提供了更大的灵活性。

示例代码:

import logging


def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == 'error':
                logging.error("%s is running" % func.__name__)
            return func(*args, **kwargs)

        return wrapper

    return decorator


@use_logging(level='info')
def bar():
    print('aaa')


@use_logging(level='error')
def foo():
    print('bbb')


bar()
foo()

 运行结果:

        上述代码中的use_logging是允许带参数的装饰器。实际上是对原有装饰器的一个函数封装,并返回一个装饰器。可以将它理解为一个含有参数的装饰器。当使用@use_logging(level="error")调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器的环境中。

 关于带参数的装饰器,详见博文:带有参数的装饰器_IT之一小佬的博客-CSDN博客

 类装饰器:

        类装饰器相比于函数装饰器具有灵活度大、高内聚和封装性等优点。使用类装饰器还可以依靠类内部的__call__方法,当使用@形式时将装饰器附加到函数上,就会调用此方法。

示例代码:

class Test(object):
    def __init__(self, func):
        self._func = func

    def __call__(self, *args, **kwargs):
        print('class decorator running')
        self._func()
        print('class decorator ending')


@Test
def bar():
    print('bar')


bar()

运行结果:

 functools.wraps

        使用装饰器可以极大的复用了代码,但它也有一个缺点就是原函数的元信息不见了,比如函数的docstring、__name__、参数列表等。

示例代码:

def logged(func):
    def with_logging(*args, **kwargs):
        print(func.__name__ + 'was called')
        return func(*args, **kwargs)

    return with_logging


@logged
def test():
    """
    does some math
    :param x:
    :return:
    """
    x = 6
    return x ** 2


#  上述函数等价于
def test2():
    """
        does some math
        :param x:
        :return:
        """
    x = 5
    return x ** 2


a = test()
print(a)
b = logged(test2)
print(b())

print("*" * 100)

print(test.__name__)
print(test.__doc__)

print("*" * 100)

print(test2.__name__)
print(test2.__doc__)

运行结果:

        上述代码执行结果中不难看出,函数test被with_logging取代了,当然它的docstring,__name__等信息就变成了with_logging函数的信息了。

        出现上述问题有时候是问题是挺严重的,这时候我们可以使用functools.wraps库,wraps本身也是一个装饰器,它能够把原函数的元信息拷贝到装饰器函数中,这使用装饰器函数也有原函数一样的元信息了。

示例代码:

from functools import wraps


def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__ + 'was called')
        return func(*args, **kwargs)

    return with_logging


@logged
def test():
    """
    does some math
    :param x:
    :return:
    """
    x = 6
    return x ** 2


#  上述函数等价于
def test2():
    """
        does some math
        :param x:
        :return:
        """
    x = 5
    return x ** 2


a = test()
print(a)
b = logged(test2)
print(b())

print("*" * 100)

print(test.__name__)
print(test.__doc__)

print("*" * 100)

print(test2.__name__)
print(test2.__doc__)

 运行结果:

内置装饰器

@staticmathod、@classmethod、@property

装饰器的执行顺序:

@a
@b
@c
def f():
    pass

#  等价于
f = a(b(c(f)))

参考博文:

如何理解Python装饰器? - 知乎

最后

以上就是无私胡萝卜为你收集整理的python中装饰器的使用教程详解(wraps)的全部内容,希望文章能够帮你解决python中装饰器的使用教程详解(wraps)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(79)

评论列表共有 0 条评论

立即
投稿
返回
顶部