2013-01-19 26 views
9
的效率

可能重複:
Python FAQ: 「How fast are exceptions?」Python的 - 捕獲的異常

我記得讀了Python實現與問候例外「更好,以尋求寬恕比請求允許」的理念。根據作者的說法,這意味着Python代碼應該使用很多try - 除了子句,而不是提前確定是否要做某些可能導致異常的事情。

我剛剛在我的web應用程序中編寫了一些try - except子句,其中大部分代碼運行時會引發異常。因此,在這種情況下,提高和捕捉異常將是常態。從效率的角度來看,這是否糟糕?我還記得有人告訴我,捕捉異常會帶來很大的性能開銷。

使用try - except語句會不必要地低效,在這種語句中,您期望幾乎所有的時間都會引發異常並捕獲異常?

下面是代碼 - 它使用Django ORM檢查將用戶與各種第三方社交提供者相關聯的對象。

try: 
    fb_social_auth = UserSocialAuth.objects.get(user=self, provider='facebook') 
    user_dict['facebook_id'] = fb_social_auth.uid 
except ObjectDoesNotExist: 
    user_dict['facebook_id'] = None 

try: 
    fs_social_auth = UserSocialAuth.objects.get(user=self, provider='foursquare') 
    user_dict['foursquare_id'] = fs_social_auth.uid 
except ObjectDoesNotExist: 
    user_dict['foursquare_id'] = None 

try: 
    tw_social_auth = UserSocialAuth.objects.get(user=self, provider='twitter') 
    user_dict['twitter_id'] = tw_social_auth.uid 
except ObjectDoesNotExist: 
    user_dict['twitter_id'] = None 

第一個將很少會例外,因爲現在我們都在實施「請用facebook」作爲新用戶加入該網站的主要方法。但是,Twitter和Foursquare是可選的,以防他們想要導入朋友或追隨者,我希望大多數人不會。

我打開更好的方式來編寫這個邏輯。

+1

代碼的具體示例是什麼? –

+1

在適用的任何地方應用「更好地尋求寬恕而不是要求允許」絕對不是最好的主意。考慮一下:如果你知道95%的調用'try..catch'內的代碼會引發異常,那麼最好重寫它。 –

+1

一個說法是,「例外情況是例外情況」。我認爲,如果您大多數時間都在期待異常,那麼您可能會以錯誤的方式使用異常。 –

回答

6

無論何時你編碼都有一個平衡的問題:性能,可讀性,正確性,可擴展性,可維護性等。 不幸的是,通常不可能在同一時間在每個方向上改進代碼。例如,速度可能不快。

在Python中鼓勵try..except的原因之一是因爲您經常無法預測您的代碼可能被使用的所有方式,所以不是檢查是否存在特定的條件,而是更一般地捕獲任何可能會出現某種類型的錯誤。因此try..except可能會使您的代碼更加可重用。

但是,如果經常達到except子句,try..except也是如此。

有沒有一種方法來編寫該塊,以便不會引發異常並使用try..except捕獲不太常見的條件?

或者,如果不是,爲了效率,您可以選擇不使用try..except。編程中幾乎沒有硬性規定。你必須根據你的平衡問題選擇你的方式。

+0

+1對於編程中幾乎沒有硬性規則和快速規則。 :) –

+0

國際海事組織+1點。 – jldupont

2

如果您試圖優化此功能的速度,您應該關注可能是實際瓶頸的問題。您的三個數據庫查詢(每個查詢都會導致操作系統進行上下文切換)幾乎肯定會比捕獲異常要長一個數量級。如果你想使代碼儘可能地快,首先將所有三個數據庫查詢到一個:通過對象

auth_objects = UserSocialAuth.objects.filter(user=self, provider__in=('facebook', 'foursquare', 'twitter')) 

,然後循環。如果這三個提供者是數據庫中唯一的提供者,那麼provider__in過濾器可能是不必要的。

2

確實,捕獲一個異常是非常昂貴的(請參閱下面的某些時間點),並且您不希望將其放在程序的瓶頸中,但在您提供的示例中,捕獲異常將會是運行時的一小部分與Model.objects.get的調用相比較,該調用必須構建SQL查詢,將其傳輸到數據庫服務器,並等待數據庫報告沒有這樣的對象。

一些示例時序。函數f2引發並捕獲異常,而f1在不使用異常的情況下實現相同的功能。

d = dict() 

def f1(): 
    if 0 in d: return d[0] 
    else: return None 

def f2(): 
    try: return d[0] 
    except KeyError: return None 

>>> timeit(f1) 
0.25134801864624023 
>>> timeit(f2) 
2.4589600563049316 

而且f3試圖從數據庫中獲取一個不存在的物體通過Django的ORM(這是在同一臺機器上運行):

def f3(): 
    try: 
     MyModel.objects.get(id=999999) 
    except MyModel.DoesNotExist: 
     pass 

這需要比f2長大約400次(這樣只要我不想等待默認number=1000000迭代完成):

>>> timeit(f3, number=1000) 
1.0703678131103516