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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM