python装饰器浅谈

装饰器在python中经常使用,接下来介绍一下自己写的装饰器
装饰器分为无参装饰器有参装饰器
装饰器使用的主要是闭包的思想

1、无参装饰器

def wrapper(func):
    """
    定义一个装饰器函数
    :param func:
    :return:
    """
    print("this is wrapper.")  # debug时会发现,这一条语句是使用装饰器时就执行了

    def _wrap(*args, **kwargs):
        """
        定义一个函数,作为装饰器函数的返回值
        :param args:
        :param kwargs:
        :return:
        """
        print("this is _wrap.")  # debug时会发现,这一条语句是调用呗装饰器装饰的函数时执行了
        # 执行功能函数
        return func(*args, **kwargs)

    # 返回一个函数(本质是返回该函数的地址)
    # 使用装饰器后,func传入进来,返回后外部获取的就是_wrap这个函数了
    return _wrap


@wrapper  # 装饰器装饰
def func(*args, **kwargs):
    """

    :param args:
    :param kwargs:
    :return:
    """
    print(f"This is a test function. args:{
      
      args};kwargs:{
      
      kwargs}")


func(1, 2, 3, x=4, y=5)

运行结果:

this is wrapper.
this is _wrap.
This is a test function. args:(1, 2, 3);kwargs:{
    
    'x': 4, 'y': 5}

2、有参装饰器

可以发现的是无参装饰器中装饰器定义时形参只能是被装饰的函数,无法传入别的参数
假定有一个需求:使用装饰器实现登陆失败n次后退出登陆(实例:QQPC版登陆成功后,断网后会间隔几分钟重新登陆,知道成功登陆)

import random


def loginCheck(count=1):
    """
    一个有参装饰器,这一层函数主要是为了提前接收需要使用除了函数之外的参数
    :param count:
    :return:
    """
    if count < 1:
        count = 1

    def wrapper(func):
        """
        定义一个无参装饰器,内部使用上一层有参装饰器接收的参数来实现功能
        :param func:
        :return:
        """

        def _wrap(*args, **kwargs):
            respond = None
            for i in range(count):
                print(f"第{
      
      i}次登陆", end=";")
                respond = func(*args, **kwargs)
                # 如果成功了就直接返回该值,哪怕是最后返回该值就会使用
                if respond:
                    print("登陆成功.")
                    return respond

                print("登陆失败")

            else:
                # for循环正常执行结束就说明没有登陆成功,返回的也应该是func的返回值
                return respond

        # 返回实现的函数
        return _wrap

    # 返回无参装饰器
    return wrapper


@loginCheck(10)  # 调用有参装饰器
def login(*args, **kwargs):
    # print(f"*args:{args}, **kwargs:{kwargs}")
    n = random.randint(-10, 10)
    if n > 0:
        return True
    else:
        return False


login()

运行结果(随机的,可以多运行几次):

0次登陆;登陆失败
第1次登陆;登陆失败
第2次登陆;登陆失败
第3次登陆;登陆失败
第4次登陆;登陆成功.

注:这是为了举例子,使用这种方式检查是否登陆成功,别喷。

很容易发现有参装饰器就是在无参装饰器外添加一层函数用来接收装饰器应该接收的参数

3、from functools import wraps

为什么要使用wraps?
每个函数有两个属性
name:返回函数的名字
doc:返回函数的注释部分
使用装饰器装饰后的函数的这两个属性就变成了装饰器的内容,我们使用装饰器后不想让这两个返回的是装饰器的,还想让这两个属性返回的是原函数的属性,需要使用到functiontools中的wraps装饰器,使用方式如下:

from functools import wraps


# ======================wraps========================#
# 每个函数都有自己的属性,使用装饰器之后,就相当于变了属性
# 使用functools.wraps可以让装饰器的属性和原函数一样
# ======================wraps========================#


def wrapper1(func):
    """
    a test wrapper
    :param func:
    :return:
    """
    print("一个普通的无参装饰器")

    def _wrapper(*args, **kwargs):
        """
        a test _wrapper
        :param args:
        :param kwargs:
        :return:
        """
        func(*args, **kwargs)

    return _wrapper


def wrapper2(func):
    """
    a test wrapper with wraps
    :param func:
    :return:
    """
    print("使用wraps后的无参装饰器")

    @wraps(func)
    def _wrapper(*args, **kwargs):
        """
        a test _wrapper
        :param args:
        :param kwargs:
        :return:
        """
        func(*args, **kwargs)

    return _wrapper


def func1(*args, **kwargs):
    """
    a test function1
    :param args:
    :param kwargs:
    :return:
    """
    print(f"this is a function. args:{
      
      args}; kwargs:{
      
      kwargs}")


@wrapper1
def func2(*args, **kwargs):
    """
    a test function2
    :param args:
    :param kwargs:
    :return:
    """
    print(f"this is a function. args:{
      
      args}; kwargs:{
      
      kwargs}")


@wrapper2
def func3(*args, **kwargs):
    """
    a test function3
    :param args:
    :param kwargs:
    :return:
    """
    print(f"this is a function. args:{
      
      args}; kwargs:{
      
      kwargs}")


# 普通函数
print(func1.__name__)
print(func1.__doc__)

# 使用普通的无参装饰器后的效果
print(func2.__name__)
print(func2.__doc__)

# 使用wraps的无参装饰器的效果
print(func3.__name__)
print(func3.__doc__)

运行结果:

__name__: func1
__doc__:
 
    a test function1
    :param args:
    :param kwargs:
    :return:
    
__name__: _wrapper
__doc__:
 
        a test _wrapper
        :param args:
        :param kwargs:
        :return:
        
__name__: func3
__doc__:
 
    a test function3
    :param args:
    :param kwargs:
    :return:

可以看到使用wraps后的装饰器装饰的函数的name和doc属性和原函数一致

猜你喜欢

转载自blog.csdn.net/weixin_50727642/article/details/128750108
今日推荐