一些实用的Python装饰器


=Start=

之前在学习Python装饰器的时候看了很多文章,但没有及时整理、总结,最近在写程序的时候发现很多装饰器具有较强的实用价值,所以抽出时间整理如下:

函数执行耗时统计
import time
def timing(f):
    def wrap(*args):
        print '<function name: {0}>'.format(f.func_name)
        time1 = time.time()
        ret = f(*args)
        time2 = time.time()
        print '[timecosts: {0} ms]'.format((time2-time1)*1000.0)
        return ret
    return wrap

使用方法:

@timing
def hello(name):
    print "hello, %s" % name

hello('tom')
给函数调用做缓存
from functools import wraps
def memo(fn):
    cache = {}
    miss = object()
    @wraps(fn)
    def wrapper(*args):
        result = cache.get(args, miss)
        if result is miss:
            result = fn(*args)
            cache[args] = result
        return result
    return wrapper

使用方法:

@memo
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

fib(100) #这里其实可以使用上面的函数耗时统计的decorator进行耗时比对
fib(101) #用以判断memo装饰器是否起到作用
失败重试函数
import time
import math

# Retry decorator with exponential backoff
def retry(tries, delay=3, backoff=2):
    '''Retries a function or method until it returns True.
    delay sets the initial delay in seconds, and backoff sets the factor by which
    the delay should lengthen after each failure. backoff must be greater than 1,
    or else it isn't really a backoff. tries must be at least 0, and delay
    greater than 0.'''
    if backoff <= 1:
        raise ValueError("backoff must be greater than 1")
    tries = math.floor(tries)
    if tries < 0:
        raise ValueError("tries must be 0 or greater")
    if delay <= 0:
        raise ValueError("delay must be greater than 0")
    def deco_retry(f):
        def f_retry(*args, **kwargs):
            mtries, mdelay = tries, delay # make mutable
            rv = f(*args, **kwargs) # first attempt
            while mtries > 0:
                if rv is True: # Done on success
                    return True
                mtries -= 1      # consume an attempt
                time.sleep(mdelay) # wait...
                mdelay *= backoff  # make future wait longer
                rv = f(*args, **kwargs) # Try again
            return False # Ran out of tries :-(
        return f_retry # true decorator -> decorated function
    return deco_retry  # @retry(arg[, ...]) -> true decorator

使用方法:

@retry(5)
def some_func():
    pass

some_func()
超时退出函数

这个函数的作用在于可以给任意可能会hang住的函数添加超时功能,这个功能在编写外部API调用 、网络爬虫、数据库查询的时候特别有用

import signal, functools
class TimeoutError(Exception): pass #定义一个Exception,在超时情况下抛出

def timeout(seconds, error_message = 'Function call timed out'):
    def decorated(func):
        def _handle_timeout(signum, frame):
            raise TimeoutError(error_message)
        def wrapper(*args, **kwargs):
            signal.signal(signal.SIGALRM, _handle_timeout)
            signal.alarm(seconds)
            try:
                result = func(*args, **kwargs)
            finally:
                signal.alarm(0)
            return result
        return functools.wraps(func)(wrapper)
    return decorated

使用方法:

@timeout(5) #限定下面的slowfunc函数如果在5s内不返回就强制抛TimeoutError Exception结束
def slowfunc(sleep_time):
    import time
    time.sleep(sleep_time) #这个函数就是休眠sleep_time秒

slowfunc(3)     #sleep 3秒,正常返回,没有异常
slowfunc(10)   #被终止
打印调试信息函数
import sys, os, linecache
def trace(f):
    def globaltrace(frame, why, arg):
        if why == "call": return localtrace
        return None
    def localtrace(frame, why, arg):
        if why == "line":
            filename = frame.f_code.co_filename
            lineno = frame.f_lineno
            bname = os.path.basename(filename)
            print "{}({}): {}".format(bname, lineno, linecache.getline(filename, lineno)),
        return localtrace
    def _f(*args, **kwds):
        sys.settrace(globaltrace)
        result = f(*args, **kwds)
        sys.settrace(None)
        return result
    return _f

使用方法:

@trace
def simple_print():
    print 1
    print 22
    print 333

simple_print() #调用
参考链接:

=END=


《 “一些实用的Python装饰器” 》 有 4 条评论

  1. Python装饰器的前世今生
    http://zhangchuzhao.site/2018/05/25/python-decorator/
    `
    一、史前故事
    二、开天辟地
    三、Pythonic世界的初探
    四、多元化百家争鸣
    1、带参数的装饰器
    2、让装饰器同时支持带参数或不带参数
    3、类装饰器
    4、装饰函数 -> 装饰类
    五、上古神器
    1、@property -> getter/setter方法
    2、@classmethod、@staticmethod
    3、@functools.wraps
    4、Easter egg
    `

  2. Python @property decorator
    https://www.programiz.com/python-programming/property

    How does the @property decorator work?
    https://stackoverflow.com/questions/17330160/how-does-the-property-decorator-work

    https://docs.python.org/2/library/functions.html#property
    https://docs.python.org/3/howto/descriptor.html

    使用@property
    https://www.liaoxuefeng.com/wiki/897692888725344/923030547069856
    `
    @property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

    @property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作。
    `
    Python property() 函数
    https://www.runoob.com/python/python-func-property.html

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注