Python實(shí)現(xiàn)動(dòng)態(tài)實(shí)例化的示例代碼
前言
最近在查一個(gè)服務(wù)的問(wèn)題時(shí),看到有一段代碼if .. elif ...
寫(xiě)了近百行,類(lèi)似
if command == "xxx": obj = CommandX() obj.run() # ... elif command == "yyy": obj = CommandY() obj.run() # ... elif command == "zzz": obj = CommandZ() obj.run() # ... # ...
翻了下git記錄,最開(kāi)始其實(shí)只有兩三個(gè)條件判斷,后來(lái)command越加越多,就這么延續(xù)下來(lái)了。
代碼邏輯其實(shí)沒(méi)什么問(wèn)題,也很簡(jiǎn)單明了,就是看起來(lái)有點(diǎn)丑,而且我還開(kāi)了比較高的桌面縮放,導(dǎo)致一屏幕幾乎都是這段if ... elif
看來(lái)看去越發(fā)覺(jué)得丑,先寫(xiě)個(gè)demo看看能不能跑通代碼。
方式1:字典映射
如果需要判斷的條件比較少,用字典做映射還是挺方便的,但如果條件多,看起來(lái)還是挺丑的。
from abc import ABC, abstractmethod class AbstractCommand(ABC): @abstractmethod def run(self): pass class CommandA(AbstractCommand): def run(self): return "this is command A" class CommandB(AbstractCommand): def run(self): return "this is command B" class CommandC(AbstractCommand): def run(self): return "this is command C" class CommandFactory: @staticmethod def create_command(command_type: str) -> AbstractCommand: command_mapping = { "cmda": CommandA, "cmdb": CommandB, "cmdc": CommandC } cls = command_mapping.get(command_type.lower()) if not cls: raise ValueError(f"Unknown command type: {command_type}") return cls() if __name__ == "__main__": cmd = CommandFactory.create_command("cmda") assert cmd.run() == "this is command A" cmd = CommandFactory.create_command("cmdb") assert cmd.run() == "this is command B" cmd = CommandFactory.create_command("cmdc") assert cmd.run() == "this is command CD" # should be exception cmd = CommandFactory.create_command("cmdd") # should be exception assert cmd.run() == "this is command D"
方式2: __init_subclass__
《流暢的Python(第2版)》的最后一章提到了這個(gè)__init__subclass__
,根據(jù)python官方文檔:
當(dāng)所在類(lèi)派生子類(lèi)時(shí)此方法就會(huì)被調(diào)用。cls 將指向新的子類(lèi)。如果定義為一個(gè)普通實(shí)例方法,此方法將被隱式地轉(zhuǎn)換為類(lèi)方法。傳給一個(gè)新類(lèi)的關(guān)鍵字參數(shù)會(huì)被傳給上級(jí)類(lèi)的 __init_subclass__
。 為了與其他使用 __init_subclass__
的類(lèi)兼容,應(yīng)當(dāng)去掉需要的關(guān)鍵字參數(shù)再將其他參數(shù)傳給基類(lèi)。
借助這個(gè)機(jī)制,可以在實(shí)現(xiàn)抽象基類(lèi)時(shí)自動(dòng)注冊(cè)子類(lèi),避免手動(dòng)維護(hù)注冊(cè)表。
from abc import ABCMeta, abstractmethod from threading import Lock from collections import UserDict class ThreadSafeDict(UserDict): """線程安全的字典""" def __init__(self): super().__init__() self._lock = Lock() def __setitem__(self, key, item): with self._lock: super().__setitem__(key, item) class Command(metaclass=ABCMeta): registry = ThreadSafeDict() def __init__(self): pass @abstractmethod def run(self): pass def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.registry[cls.__name__.lower()] = cls # 自動(dòng)注冊(cè)子類(lèi) # 子類(lèi)定義即自動(dòng)注冊(cè) class CommandA(Command): def run(self): return "this is command a!" class CommandB(Command): def run(self): return "this is command b!" class CommandC(Command): def run(self): return "this is command b!" def create_command(command_type: str) -> Command: """工廠函數(shù)""" cls = Command.registry.get(command_type.lower()) if not cls: raise ValueError(f"Unknown command type: {command_type}") return cls() if __name__ == "__main__": cmd = create_command("CommandA") assert cmd.run() == "this is command a!" cmd = create_command("CommandB") assert cmd.run() == "this is command b!" cmd = create_command("CommandC") assert cmd.run() == "this is command cc!" # should be exception cmd = create_command("CommandD") assert cmd.run() == "this is command b!" # should be exception
乍一看還是挺不錯(cuò)的,但是也有個(gè)缺點(diǎn),那就是如果各個(gè)類(lèi)分散在不同模塊中,那么工廠函數(shù)所在的模塊就要寫(xiě)一堆from xxx import ...
如果module和類(lèi)命名比較規(guī)范,也可以這么動(dòng)態(tài)加載類(lèi)
import importlib def create_class(module_name, class_name): module = importlib.import_module(module_name) cls = getattr(module, class_name) return cls()
補(bǔ)充
自動(dòng)注冊(cè)類(lèi)看起來(lái)炫,但是對(duì)代碼閱讀來(lái)說(shuō)不是很直觀。易讀還是美觀?這是一個(gè)問(wèn)題。
到此這篇關(guān)于Python實(shí)現(xiàn)動(dòng)態(tài)實(shí)例化的示例代碼的文章就介紹到這了,更多相關(guān)Python動(dòng)態(tài)實(shí)例化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python使用mitmproxy抓取瀏覽器請(qǐng)求的方法
今天小編就為大家分享一篇python使用mitmproxy抓取瀏覽器請(qǐng)求的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-07-07Django 實(shí)現(xiàn)admin后臺(tái)顯示圖片縮略圖的例子
今天小編就為大家分享一篇Django 實(shí)現(xiàn)admin后臺(tái)顯示圖片縮略圖的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-07-07python多進(jìn)程并發(fā)demo實(shí)例解析
這篇文章主要介紹了python多進(jìn)程并發(fā)demo實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12Scrapy將數(shù)據(jù)保存到Excel和MySQL中的方法實(shí)現(xiàn)
本文主要介紹了Scrapy將數(shù)據(jù)保存到Excel和MySQL中的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02python日期時(shí)間轉(zhuǎn)為字符串或者格式化輸出的實(shí)例
今天小編就為大家分享一篇python日期時(shí)間轉(zhuǎn)為字符串或者格式化輸出的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05Python爬蟲(chóng)分析微博熱搜關(guān)鍵詞的實(shí)現(xiàn)代碼
這篇文章主要介紹了Python爬蟲(chóng)分析微博熱搜關(guān)鍵詞的實(shí)現(xiàn)代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02python實(shí)現(xiàn)掃描日志關(guān)鍵字的示例
下面小編就為大家分享一篇python實(shí)現(xiàn)掃描日志關(guān)鍵字的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04