目錄
- getattr詳解
- 前言
- 一.消除if…else
- 二. 反射機制
- 三.getattr 詳解
- 總結
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 = https://www.cnblogs.com/failymao/p/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 = https://www.cnblogs.com/failymao/p/json.loads(response.body.decode())
return data
else:
return self.response

簡化后的代碼,通過getattr判斷傳入的disk_type引數, 映射map中對應的 屬性. 從而優化代碼.
總結
getattr 內置屬性加map方式可以簡化if…else邏輯代碼. 本代碼并未考慮代碼的性能因素. 抽時間比較一下.
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/177283.html
標籤:Python
上一篇:分布式任務佇列 1
