如何實現一個輕量外掛系統
阿新 • • 發佈:2022-03-10
假設我們實現了一個程式,它從 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()
的迴圈中,每次迴圈返回的是一個字典,這個字典包含很多項,例如age
、date
等等。我們需要設計一些邏輯對這個資料進行處理或者過濾。
但這些邏輯是逐漸增加,一開始只有一個需求,就是如果發現doc
的age
欄位中,如果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/A
;date
小於2020-05-01
的資料就直接丟棄了。
轉自:微信公眾號:未聞code