2017-07-27 73 views
1

我構造了我的類來解析消息,這是一個字典(可能來自JSON對象),有關更多信息,請參見pastebin。 因爲我會得到這些對象的許多,我不想處理它們全部 我認爲跳過初始化方法的一些步驟。所以我寫了一個條件初始化類。 我把一個glance標誌,如果處於活動狀態將只處理7個可能的成員中的3個。 我希望保持選項稍後查看參數,因此我還想把opt標誌只存儲消息進行進一步處理。部分構造函數初始化的性能

class CbkQuery: 

    def __init__(self, query_msg, glance = False, opt = True):   
     #required arguments 
     self.id = query_msg['id'] 
     self.person = Person(query_msg['from']) 
     self.chat_instance = query_msg['chat_instance'] 

     if glance: 
      pass 
     else: 
      self.query_msg = query_msg 
      if opt: 
       # optional arguments 
       self.data = self.query_msg.get('data') 
       self.message = self.query_msg.get('message') 
       if self.message is not None: 
        self.message = Message(self.message) 
       self.inline_message_id = self.query_msg.get('inline_message_id') 
       self.game_short_name = self.query_msg.get('game_short_name') 

現在,因爲我沒有這個噱頭實際上有一個性能增益,我決定記錄的性能(是一個自定義日誌類我做了一個使用perf_counter(),我反正檢查多次,在時間的差異顯得穩重)

glance processing took: (glance flag True, opt flag False) 
Elapsed time for subprocess: 00:00:01.749 

store query, processing took: (glance flag False, opt flag False) 
Elapsed time for subprocess: 00:00:00.182 

build complete class processing took: (glance flag False, opt flag True 
Elapsed time for subprocess: 00:00:01.322 

「驚喜驚喜,一眼類需要更長的時間,那麼所有其他(?!?!?),最快的是存儲‘的類未處理消息’。 有人可以解釋我爲什麼嗎?這是否是因爲存儲未處理的消息實際上使淺拷貝而不是副本?

+0

我使用了模塊時間和perf_counter,我知道它不是精確的,但是它們的差異爲1秒,而且我感興趣的是底部時間我的電腦需要什麼時間來完成該操作,而不是時間(如去除.sleep())等......(至少這是使用perf而不是其他更準確的測量方法的原因) – Pella86

+1

Oups,我想問你是否使用了模塊' timeit'。它允許Python代碼的精確計時,因爲它會自動執行一些執行並計算平均值。 –

回答

2

爲什麼第一次測試花費的時間超出了我的理解範圍(但是您的代碼在使用負面測試而不是passelse陳述時可讀性更強)。

但爲什麼它是最快的只是「存儲未處理消息」是相當明顯的:它是一個單一的操作(存儲在實例的__dict__query_msg參考),而不是N次從query_msg得到一個值,並存儲該參考值在實例的__dict__中。

這與深層或淺層副本無關 - Python從不「複製」任何東西,而是明確要求 - 在這兩種情況下,它只存儲對對象的引用。

作爲一個側面說明,如果query_msgdict和你們班主要是爲它的包裝,你可以只建立您的實例的__dict__query_msg,即:

def __init__(self, query_msg): 
    self.__dict__.update(query_msg) 
    self.person = Person(query_msg["from"]) 
    self.message = Message(self.message) 

或者如果你確定以貿易屬性訪問時間instanciation時間,只要保持裁判query_msg並添加屬性來訪問它的值屬性:

class CbkQuery(object): 
    def __init__(self, query_msg): 
     self._query_msg = query_msg 

    @property 
    def id(self): 
     return self._query_msg["id"] 

    @property 
    def chat_instance(self): 
     return self._query_msg["chat_instance"] 

    # etc 

    @property 
    def person(self): 
     # here we cache the `Person` instanciation 
     # to avoid repeating it on each and every access 
     if not hasattr(self, "_person"): 
      self._person = Person(self._query_msg["from"]) 
     return self._person 

    # etc 

WRT /你的標杆,我得到非常不同的R esults使用timeit(與Person mock實現和Message):

class Person(object): 
    def __init__(self, data): 
     self.data = data 

class Message(object): 
    def __init__(self, data): 
     self.data = data 

class CbkQuery(object): 
    def __init__(self, query_msg, glance=False, opt=True):   
     self.id = query_msg['id'] 
     self.person = Person(query_msg['from']) 
     self.chat_instance = query_msg['chat_instance'] 

     if glance: 
      pass 
     else: 
      self.query_msg = query_msg 
      if opt: 
       self.data = self.query_msg.get('data') 
       self.message = self.query_msg.get('message') 
       if self.message is not None: 
        self.message = Message(self.message) 
       self.inline_message_id = self.query_msg.get('inline_message_id') 
       self.game_short_name = self.query_msg.get('game_short_name') 


qmsg = { 
    "id":"id", 
    "from":"[email protected]", 
    "chat_instance":"chat_instance", 
    "data":"data", 
    "message":"message", 
    'inline_message_id':'inline_message_id', 
    "game_short_name":"game_short_name" 
    } 


if __name__ == "__main__": 
    import timeit 

    print("True, False: {}".format(timeit.timeit("CbkQuery(qmsg, True, False)", "from __main__ import CbkQuery, qmsg"))) 
    print("False, False: {}".format(timeit.timeit("CbkQuery(qmsg, False, False)", "from __main__ import CbkQuery, qmsg"))) 
    print("False, True: {}".format(timeit.timeit("CbkQuery(qmsg, False, True)", "from __main__ import CbkQuery, qmsg"))) 

其產生以下結果(與Python 2.7.6和3.4.3):2.7.x之間

[email protected]:~/Work/playground$ python whythefuck.py 
True, False: 0.732594013214 
False, False: 0.785747051239 
False, True: 1.89674901962 


[email protected]:~/Work/playground$ python3 whythefuck.py 
True, False: 0.776961212977767 
False, False: 0.8794295950210653 
False, True: 1.832904842973221 

的小差異和3.x,但它與預期的一致性更高 - 代碼執行的指令越多,所需的時間越長;)

我明確地認爲存在一些與測試相關的事情...

+0

謝謝你的建議。實際上,我是第一次在代碼中廣泛使用字典。我正在學習如何使用發送消息的API,所以這些字段也是某種類型的備忘錄,但我正在尋找'.update()'方法和'@ propriety'裝飾器。我不明白的是,所有這三個構造函數確實會把一些東西放在一個新的屬性中......但是如果整個輸入被存儲起來,它似乎會得到提升。可能是某種優化?哦,看這個人正在製作一個包裝 - >讓我們使用原件? – Pella86

+0

我可以告訴你,Python永遠不會嘗試做這種類型的任何「優化」。正如我所說的,我無法理解您從基準測試中獲得的結果,並懷疑測試代碼中某處出現了邏輯錯誤(偶然發生了某些緩存問題 - Python確實有一些問題)。 –

+0

我也很困惑。 https://pastebin.com/YLhxEmHc這裏是我寫的記錄器(我可以使用'日誌記錄',但由於某種原因,它沒有打印的東西到IPython控制檯,我不想尋找配置... – Pella86