1. 程式人生 > 其它 >如何實現一個輕量外掛系統

如何實現一個輕量外掛系統

假設我們實現了一個程式,它從 Redis 讀取資料,然後寫入 MongoDB。一開始程式是這樣的:

def read_from_redis():
    ...

def write_to_mongodb(doc):
    ...

def parse():
    for doc in self.read_from_redis():
        self.write_to_mongodb(doc)

for doc in self.read_from_redis()的迴圈中,每次迴圈返回的是一個字典,這個字典包含很多項,例如agedate等等。我們需要設計一些邏輯對這個資料進行處理或者過濾。

但這些邏輯是逐漸增加,一開始只有一個需求,就是如果發現docage欄位中,如果age不是數字且不能轉換為數字,那麼需要把它改成N/A

後來又增加了一個新的需求,如果doc裡面的date欄位對應的日期小於2020-05-01,那麼這條資料直接丟棄。

接下來還要新增很多其他的需求。為了避免反覆修改程式碼,我們可以實現一個輕量級的外掛系統。

我們先實現呼叫這個外掛系統的部分:

plugins = {}


def read_from_redis():
    datas = [
        {'age': 34, 'name': 'xxx', 'date': '2020-05-10'},
        {
'age': 12, 'name': 'yyy', 'date': '2020-04-03'}, {'age': '23', 'name': 'zzz', 'date': '2020-05-12'}, {'age': 'aa', 'name': 'abc', 'date': '2020-05-10'}, {'age': 89, 'name': 'def', 'date': '2020-02-10'}, {'age': '', 'name': 'xyz', 'date': '2020-05-10'}, {'age': 'xy', 'name': 'xxx
', 'date': '2020-05-10'}, {'age': 'mp', 'name': 'xxx', 'date': '2020-05-10'}, {'age': 34, 'name': 'xxx', 'date': '2019-02-10'}, ] for data in datas: yield data def write_to_mongodb(doc): print(f'正在把資料:{doc} 寫入到 MongoDB 中') def parse(): for doc in read_from_redis(): for name, plugin in plugins.items(): print(f'正在執行外掛:{name}') doc = plugin(doc) if not doc: print(f'資料: {doc},被外掛:{name}過濾。') continue write_to_mongodb(doc) if __name__ == '__main__': parse()
 

看到這裡,你會不會覺得很奇怪,這裡的plugins不是一個空字典嗎?那你下面的 for 迴圈怎麼能夠執行呢?

不慌,我們現在使用裝飾器把外掛註冊plugins中:

def register(plugin):
    plugins[plugin.__name__] = plugin
    return plugin

@register
def transfer_age_to_int(doc):
    try:
        doc['age'] = int(doc['age'])
    except ValueError:
        doc['age'] = 'N/A'
    return doc

@register
def filter_date(doc):
    date = doc['doc']
    if date < '2020-05-01':
        return None
    return doc

這個裝飾器,它的作用就是把它裝飾的函式存到plugins字典中。所以當我們使用裝飾器裝飾一個 plugin 函式的時候,它就已經被自動註冊到plugins字典中了。不需要我們再手動存放一次。

下面我們來實際執行一下:

 

 

可以看到,不是數字且不能被轉換為數字的age欄位的值被改成了N/Adate小於2020-05-01的資料就直接丟棄了。

轉自:微信公眾號:未聞code