Python 装饰器

装饰器

先来看一个代码段:

def announce(f):
    def wrapper(name):
        print("start to run the function...")
        f(name)
        print("Done with the function.")
    return wrapper

@announce
def hello(name):
    print(f"hello, {name}")

name = "Sudley"
print(f"function name: {hello.__name__}")
hello(name)

在这里插入图片描述

@announce就是装饰器,通过装饰器可以快速的给函数添加功能。装饰器让你在一个函数的前后去执行代码。
上面是装饰器可以理解为hello=announce(hello),所以hello(name)相当于wrapper(name)
上面查看hello的函数名称是announce中返回的函数名称wrapper,也行你想hello依然是hello可以使用functools.wraps来修饰一下,@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。

from functools import wraps

def announce(f):
    @wraps(f)
    def wrapper(name):
        print("start to run the function...")
        f(name)
        print("Done with the function.")
    return wrapper

@announce
def hello(name):
    print(f"hello, {name}")

name = "Sudley"
print(f"function name: {hello.__name__}")
hello(name)

在这里插入图片描述

装饰器常见用法举例

1.日志(logging):

平常可能会遇到需要定义函数开始运行和介绍运行的日志,如

import logging
logging.basicConfig(level=logging.INFO)
def square_func(x):
    logging.info("square_func was called")
    return x * x

def add_func(x, y):
    logging.info("add_func was called")
    return x + y

s_result = square_func(4)
a_result = add_func(2, 3)
print(s_result, a_result)


可以看到上面的logging.info有一点坏味道,代码重复了,这时就可以用到装饰器进行优化,如下

from functools import wraps
import logging
logging.basicConfig(level=logging.INFO)
def logit(func):
    @wraps(func)
    def with_called_logging(*args, **kwargs):
        logging.info(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_called_logging

@logit
def square_func(x):
    return x * x

@logit
def add_func(x, y):
    return x + y

s_result = square_func(4)
a_result = add_func(2, 3)
print(s_result, a_result)

2.函数或方法失败重试(retry):

当通过wget从网站上download文件时,也许会因为服务、网络不稳定或者其他原因导致失败,也许是遇到性能瓶颈或者暂时无法找到根因,这时sleep一段时间并retry也许能规避当前问题

import time

def retry(func):
    def retry_when_fail(*args, **kwargs):
        retry_times = 5
        for times in range(retry_times):
            if not func(*args, **kwargs):
                if times != (retry_times - 1):
                    print("run %s failed,retry times %s" % (func.__name__, times))
                    time.sleep(3)
                    continue
                else:
                    print("run %s failed.exit" % func.__name__)
                    raise
            print("run %s success." % func.__name__)
            break
        return func(*args, **kwargs)

    return retry_when_fail

@retry
def download_tool(ret):
    #download sth
    return ret

if __name__ == '__main__':
    #ret = True
    ret = False
    download_tool(ret)

ret = True和False的运行演示如下
在这里插入图片描述

带参数的装饰器

前面我们用到的@wraps(func)就是带参数的装饰器,允许传入func参数,下面我们定义一个类似的装饰器。在前面日志的基础上允许添加日志级别的参数。

import logging
logging.basicConfig(level=logging.INFO)

def logit(level="info"):
    def decorator(func):
        def with_called_logging(*args, **kwargs):
            if level == "info":
                logging.info(func.__name__ + " was called")
            elif level == "warn":
                logging.warn(func.__name__ + " was called")
            return func(*args, **kwargs)
        return with_called_logging

    return decorator

@logit()
def square_func(x):
    return x * x

@logit("warn")
def add_func(x, y):
    return x + y

s_result = square_func(4)
a_result = add_func(2, 3)
print(s_result, a_result)

在这里插入图片描述

这里相比之前多封装了一层函数,可以理解为含有参数的闭包。
由于logit()=decorator
所以@logit()、@logit(“warn”)都等于@decorator,日志级别相关参数通过闭包的形式传入。

类装饰器

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

import logging
logging.basicConfig(level=logging.INFO)

def logit(level="info"):
    class decorator(object):
        def __init__(self, func, level = "info"):
            self.level = level
            self._func = func
        def __call__(self,*args):
            if self.level == "info":
                logging.info(self._func.__name__ + " was called")
            elif self.level == "warn":
                logging.warn(self._func.__name__ + " was called")
            return self._func(*args)
    return decorator

@logit()
def square_func(x):
    return x * x

@logit(level = "warn")
def add_func(x, y):
    return x + y

s_result = square_func(4)
a_result = add_func(2, 3)
print(s_result, a_result)

__call__方法(补充知识点)

创建类型的时候定义了__call__()方法,这个类型就是可调用的。

import logging
logging.basicConfig(level=logging.INFO)

class decorator(object):
    def __init__(self, func, level = "info"):
        self.level = level
        self._func = func
    def __call__(self,*args):
        if self.level == "info":
            logging.info(self._func.__name__ + " was called")
        elif self.level == "warn":
            logging.warn(self._func.__name__ + " was called")
        return self._func(*args)

#@decorator
def square_func(x):
    return x * x

square_func = decorator(square_func)
print(type(square_func))
s_result = square_func(4)           #类的实例可以直接调用
print(s_result)

在这里插入图片描述
不含__call__方法的类型举例:

import logging
logging.basicConfig(level=logging.INFO)

class decorator(object):
    def __init__(self, func, level = "info"):
        self.level = level
        self._func = func
    def not_call(self,*args):
        if self.level == "info":
            logging.info(self._func.__name__ + " was called")
        elif self.level == "warn":
            logging.warn(self._func.__name__ + " was called")
        return self._func(*args)

#@decorator
def square_func(x):
    return x * x

square_func = decorator(square_func)
print(type(square_func))
try:
    s_result = square_func(4)
except TypeError as e:
    print(e)
s_result = square_func.not_call(4)
print(s_result)

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值