[TOC] getattr詳解 前言 這兩天在優化騰訊雲遷移平臺( "SmartMS" )的中間件( )時. 其中某些介面由於涉及多種伺服器系統類型, 遷移類型的判斷.導致往往一個介面動輒70 80行. 隨便進行一個介面的修改, 調試, 參數的變更. 都將花費好幾分鐘的時間去縷縷中間的邏輯.加上同一 ...
目錄
getattr詳解
前言
這兩天在優化騰訊雲遷移平臺(SmartMS)的中間件(go2cloud_api
)時. 其中某些介面由於涉及多種伺服器系統類型, 遷移類型的判斷.導致往往一個介面動輒70-80行. 隨便進行一個介面的修改, 調試, 參數的變更. 都將花費好幾分鐘的時間去縷縷中間的邏輯.加上同一個介面, 不同系統類型(主要是windows, Linux)涉及的技術棧不一樣,同一個介面就有兩個不同的代碼風格的人進行編寫. 最終導致代碼結構, 函數,類亂七八糟. 剛好疫情期間, 人在湖北(欲哭無淚呀),時間充裕, 想著來進行一次代碼重構. 先看一下最初的代碼.
上面的代碼:只是windows的部分代碼. 因為還涉及到介面的公共參數校驗, 不同遷移系統類型調用agent不同介面. 同一個介面使用了大量的if…else.
首先說明,介面本身並不複雜. 核心技術不在本篇範疇內. 有興趣研究代碼的或試用騰訊雲遷移平臺的(SmartMS),可以留言. 免費體驗.
一.消除if…else
因為代碼中涉及到太多的if…else. 本來想使用策略模式 + 工廠模式 (消除if…else). 但是由於項目起始沒有考慮代碼設計模式, 導致現在改動會花費增加很大的工作量. 所以只能想其他的辦法.
之前對
jumpserver
進行二次開發時, 研究過核心代碼. 發現代碼中使用了大量的getattr
, 裡面getattr主要是用在函數調用上. 比如下麵我截取部分代碼def run_command(self, func_name, args): """ Attempts to run the given command. If the command does not execute, or there are any problems validating the given GET vars, an error message is set. func: the name of the function to run (e.g. __open) command_variables: a list of 'name':True/False tuples specifying which GET variables must be present or empty for this command. """ if not self.check_command_args(args): self.response['error'] = 'Invalid arguments' print("++++++++++++++++++++++++++++++++ not valid") return func = getattr(self, '_' + self.__class__.__name__ + func_name, None) if not callable(func): self.response['error'] = 'Command failed' return try: func() except Exception as e: self.response['error'] = '%s' % e logger.error("Error occur ------------------------------") logger.exception(e) logger.error("Error end ------------------------------")
getattr
是python里的一個內建函數,在python的官方文檔中:getattr()的解釋:getattr(object, name[, default])
Return the value of the named attribute of object. name must be a string. If the string is the name of one of the object’s attributes, the result is the value of that attribute. For example, getattr(x, ‘foobar’) is equivalent to x.foobar. If the named attribute does not exist, default is returned if provided, otherwise AttributeError is raised.
getattr()
這個方法最主要的作用是實現反射機制。也就是說可以通過字元串獲取方法實例。這也是python中的所說的自省
這樣,- 你就可以把一個類可能要調用的方法放在配置文件里,在需要的時候動態載入。
python裡面跟
getattr
相關的有hasattr,setattr,delattr
,那麼我們通過下麵的例子,來詳細的說說他們的用法。
二. 反射機制
hasattr(object,name)
bool 判斷object中是否具有name屬性,例如:
foo = test() hasattr(foo,’setName’) #判斷setName是否存在,存在則返回True。
2.getattr(object,name,default)
如果存在name屬性(方法)則返回name的值(方法地址)否則返回default值。
# foo.py def name(): return "hello" # main.py import foo getattr(foo,'name',"default value") # 存在name屬性,所以返回其value # "hello" getattr(foo,'test',None) # None
這裡函數的調用方式.
3.setattr(object,name,default)
為類設置一個新的屬性
class Foo(object): def __init__(self,sex): self.sex = sex foo = Foo('man') # 實例化類 setattr(Foo,'age',18) # 設置一個新的屬性 foo.age # 18
改變原有的類屬性的值
class Foo(object): def __init__(self,sex): self.sex = sex foo = Foo('man') # 實例化類 setattr(Foo,'sex','woman') # 設置一個新的屬性 foo.sex # woman
4.delattr(object,'name')
刪除類屬性
delattr(foo,'sex') #刪除屬性sex,原值為`woman` getattr(foo,'sex','not find') #'not find''
三.getattr 詳解
本篇重點是講述getattr
的用法
1. 函數
demo.py
#!/usr/bin/env python # ~*~ coding: utf-8 ~*~ import time def hello(a, b): print('hello') return a, b def test_sleep(a, b): print('test_sleep') time.sleep(3) return a, b
main.py
#!/usr/bin/env python # ~*~ coding: utf-8 ~*~ import multiprocessing import demo def run(func, *args): print(getattr(demo, func)(*args)) if __name__ == "__main__": run('hello', "a", "b") pool = multiprocessing.Pool(processes=4) for i in range(10): pool.apply_async(run, ('hello', 'a', 'b')) pool.apply_async(run, ('test_sleep', 'a', 'b')) pool.close() pool.join() print("END ")
定義一個字元串,然後用getattr
去執行,這樣調用效果有些類似python的celery那種用globals的用法。 傳遞一個函數的名字,當然是字元串,用multiprocessing多進程調用這個方法。
2.類中使用
通過此方法優化文章開頭提供的代碼如下
類代碼
class SrcAgentClient(CommAgentClient):
...
@property
async def system_disk_sync_status(self, action="system_migration_heartbeat"):
''' Get rsync server service status.
:param action: a string,match the desAgent api:rsync_status_check
:return:
'''
url = self._api_base_url + action
if await self._agent_status:
response = await self.client.fetch(url, method="GET")
data = json.loads(response.body.decode())
return data
else:
return self.response
@property
async def data_disk_sync_status(self, action="data_migration_heartbeat"):
''' Get rsync server service status.
:param action: a string,match the desAgent api:rsync_data_status_check
:return:
'''
url = self._api_base_url + action
if await self._agent_status:
response = await self.client.fetch(url, method="GET")
data = json.loads(response.body.decode())
return data
else:
return self.response
簡化後的代碼,通過getattr判斷傳入的disk_type參數, 映射map中對應的 屬性. 從而優化代碼.
總結
getattr 內置屬性加map方式可以簡化if…else邏輯代碼. 本代碼並未考慮代碼的性能因素. 抽時間比較一下.