Python装饰器几个有用又好玩的例子

装饰器是一种巧妙简洁的魔术,类似于Java中的面向切面编程,我们可以再函数执行前、执行后、抛出异常时做一些工作。利用装饰器,我们可以抽象出一些共同的逻辑,简化代码。而简化代码的同时,就是在增加代码鲁棒性。

一、缓存

# coding:utf8
import time
import json

"""
简单的内存缓存参数
"""


def simple_cache(timeout=3):
    def decorator(f):
        def ff(*args, **kwargs):
            arg = json.dumps([args, kwargs])
            res = None
            key = f.__module__ + f.__name__ + arg
            if hasattr(f, key):
                res = getattr(f, key)
                if time.time() - res['last_time'] > timeout:
                    res = None
            if res is None:
                res = {'last_time': time.time(), 'data': f(*args, **kwargs)}
                setattr(f, key, res)
            return res['data']

        return ff

    return decorator

if __name__ == '__main__':
    @simple_cache(timeout=3)
    def haha(user_id):
        print("haha", user_id)


    @simple_cache(timeout=3)
    def baga(user_id):
        print("baga", user_id)


    haha(0)
    baga(0)
    haha(0)
    haha(1)
    time.sleep(5)
    haha(1)

二、重试

在进行网络请求时,我们常常需要重试几次才能请求成功。这种套路经常使用,却又嵌套的非常丑陋。此时,我们可以用装饰器将重试逻辑抽象出来。

def retry(count=1):
    def dec(f):
        def ff(*args, **kwargs):
            ex = None
            for i in range(count):
                try:
                    ans = f(*args, **kwargs)
                    return ans
                except Exception as e:
                    ex = e
            raise ex

        return ff

    return dec


db = []


@retry(count=10)
def until_six():
    db.append("haha")
    print("until_six")
    return db[6]


print(until_six())

三、兼容旧版参数

库都是随着时间变化而不断丰富的,函数的参数有可能发生变化,使用注解可以简洁完美地兼容旧版本。

lib.py

def add(x, y):
    return x + y

haha.py

from lib import add

z = add(x=4, y=5)
print(z)

现在库函数add需要把x变成one,y变成two,同时需要保持haha.py正常运行。
lib.py

change_list = {  # 存放函数名称变化表,统一维护
    'add': {
        'x': 'one',
        'y': 'two',
    }
}


def legacy(f):
    def ff(*args, **kwargs):
        if f.__name__ in change_list:
            for old_arg, new_arg in change_list[f.__name__].items():
                if new_arg not in kwargs and old_arg in kwargs:
                    kwargs.update({new_arg: kwargs.get(old_arg)})
                    del kwargs[old_arg]
        print(kwargs)
        return f(**kwargs)

    return ff


@legacy
def add(one, two, **kwargs):
    return one + two

这种方式可以把API的全部变化放到一个统一的配置文件里面,当决定不再支持旧版时,直接从配置文件里面把旧版参数删除掉即可。

四、deprecated

import logging


def deprecated(info):
    def decorator(f):
        def ff(*args, **kwargs):
            logging.warning("%s is deprected. %s" % (f.__name__, info))
            res = f(*args, **kwargs)
            return res

        return ff

    return decorator

原文地址:https://www.cnblogs.com/weiyinfu/p/8889804.html