博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python 装饰器
阅读量:4148 次
发布时间:2019-05-25

本文共 5451 字,大约阅读时间需要 18 分钟。

文章目录

装饰器

先来看一个代码段:

def announce(f):    def wrapper(name):        print("start to run the function...")        f(name)        print("Done with the function.")    return wrapper@announcedef 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 wrapsdef announce(f):    @wraps(f)    def wrapper(name):        print("start to run the function...")        f(name)        print("Done with the function.")    return wrapper@announcedef hello(name):    print(f"hello, {name}")name = "Sudley"print(f"function name: {hello.__name__}")hello(name)

在这里插入图片描述

装饰器常见用法举例

1.日志(logging):

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

import logginglogging.basicConfig(level=logging.INFO)def square_func(x):    logging.info("square_func was called")    return x * xdef add_func(x, y):    logging.info("add_func was called")    return x + ys_result = square_func(4)a_result = add_func(2, 3)print(s_result, a_result)

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

from functools import wrapsimport logginglogging.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@logitdef square_func(x):    return x * x@logitdef add_func(x, y):    return x + ys_result = square_func(4)a_result = add_func(2, 3)print(s_result, a_result)

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

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

import timedef 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@retrydef download_tool(ret):    #download sth    return retif __name__ == '__main__':    #ret = True    ret = False    download_tool(ret)

ret = True和False的运行演示如下

在这里插入图片描述

带参数的装饰器

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

import logginglogging.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 + ys_result = square_func(4)a_result = add_func(2, 3)print(s_result, a_result)

在这里插入图片描述

这里相比之前多封装了一层函数,可以理解为含有参数的闭包。

由于logit()=decorator
所以@logit()、@logit(“warn”)都等于@decorator,日志级别相关参数通过闭包的形式传入。

类装饰器

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

import logginglogging.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 + ys_result = square_func(4)a_result = add_func(2, 3)print(s_result, a_result)

__call__方法(补充知识点)

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

import logginglogging.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)#@decoratordef square_func(x):    return x * xsquare_func = decorator(square_func)print(type(square_func))s_result = square_func(4)           #类的实例可以直接调用print(s_result)

在这里插入图片描述

不含__call__方法的类型举例:

import logginglogging.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)#@decoratordef square_func(x):    return x * xsquare_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)

在这里插入图片描述

转载地址:http://nyiti.baihongyu.com/

你可能感兴趣的文章
HashMap源码分析
查看>>
TreeMap红黑树基础相关内容介绍
查看>>
ConcurrentHashMap 1.8源码分析(1)
查看>>
ConcurrentHashMap1.8源码源码分析(2)
查看>>
Volatile关键字介绍
查看>>
synchronized实现方式及锁优化
查看>>
wait(),notify() 和 notifyAll()使用及原理
查看>>
CAS了解及CAS容易发生的问题
查看>>
Thread类当中sleep, join ,yield方法
查看>>
ReentrantLock实现及AQS简析
查看>>
ReentrantLock使用对比Synchronized
查看>>
ReentrantReadWriteLock使用及源码分析
查看>>
StampedLock使用方式
查看>>
java中各种锁的对比Synchronized,ReentrantLock, ReentrantReadWritLock,StampedLock
查看>>
ThreadLocal使用方式,源码分析,内存泄漏
查看>>
CyclicBarrier使用及源码分析
查看>>
CountDownLatch使用及源码分析
查看>>
java并发包中Semaphore使用及源码分析
查看>>
线程池介绍和常用的线程池分析(一)
查看>>
线程池ThreadPoolExecutor源码分析(二)
查看>>