2015-06-09 35 views
0

我有以下片段,其中我找不到明顯的贏家。 你會推薦哪種款式?還有比這更好的東西嗎?只有在字典中存在密鑰時纔對值做些什麼

(假設self.task.flags是一本字典)

  1. in[]

    if 'target_names' in self.task.flags: 
        foo(self.task.flags['target_names']) 
    

    這提到了字典和密鑰的名稱兩次(違反幹)。

    我也可以想象,這會在內部執行兩次相同的查找, 在第一種情況下丟掉值(in)。

  2. get和臨時變量

    value = self.task.flags.get('target_names') 
    if value: 
        foo(value) 
    

    這消除了重複,而是一個臨時變量是不是很優雅。

    {'target_names': None}存在於self.task.flags中時,其含義也與其他值有細微的差別。

  3. 例外(「EAFP」)

    try: 
        foo(self.task.flags['target_names']) 
    except KeyError: 
        pass 
    

    聽說EAFP一般鼓勵Python,但我還是擔心 的例外可能是昂貴的,尤其是在關鍵 存在的機會不是很高。

    這也可能掩蓋在foo內引發的任何異常。

    try ... except結構可以在視覺上分散注意力;當涉及其他異常處理時,更多地使用 。

+0

這完全取決於上下文。在不同情況下,其中一種方式比其他方式更優雅。 – thefourtheye

+1

第二個假設並不好,因爲如果該值爲False或None,或者是'',0或任何空值,它將錯誤地不觸發該函數調用。 第二個只有在你知道可能的值範圍時纔有效。 –

+0

@AlexanderHuszagh:謝謝;我已經說過它的含義有所不同,儘管它沒有包含其他的錯誤值('False','0','''')。 – musiphil

回答

1

醜陋的方式:

# with impossibleValue being anything that can't possibly be one of your 
# values, like self.task.flags or an Object created here and now, like i'll do now. 
impossibleValue = Object() 

# dict.get(key, defaultValue) returns dict[key] if key 
# is in dict, otherwise returns defaultValue. 
value = self.task.flags.get('target_names', impossibleValue) 
if value != impossibleValue: 
    foo(value) 
+0

如果你打算建議這種方法,那麼至少應該通過演示如何創建IMPOSSIBLEVALUE來實現......最簡單的方法是使用sentinel = object()來保證唯一的對象,然後使用身份檢查如「如果價值不是哨兵......」 –

+0

雖然這個答案可能是正確和有用的,但如果你包含一些解釋並解釋它如何幫助解決問題,那麼這是首選。如果存在導致其停止工作並且用戶需要了解其曾經工作的變化(可能不相關),這在未來變得特別有用。 –

+0

採取點,傳入編輯 – Dleep

1

真的,就看你了。沒有實質性的性能差異,主要擔心的就是小心第二種形式。如果你的值爲空,所以它們不會觸發「if/then」子句,你會錯誤地認爲沒有鍵。

a = {k:k**2 for k in range(1000000)} 
def nullfunc(*args, **kwargs): 
    del args, kwargs 

def infunc(a): 
    if 5000 in a: 
     nullfunc(a[5000]) 

def getvalue(a): 
    value = a.get(5000) 
    if value: 
     nullfunc(value) 

def tryexcept(a): 
    try: 
     nullfunc(a[5000]) 
    except KeyError: 
     pass 


In [12]: %timeit infunc(a) 
1000000 loops, best of 3: 238 ns per loop 

In [13]: %timeit getvalue(a) 
1000000 loops, best of 3: 256 ns per loop 

In [14]: %timeit tryexcept(a) 
1000000 loops, best of 3: 197 ns per loop 

In [15]: 
+1

感謝您的實驗。另一方面,如果密鑰不存在(例如,你做'del a [5000]'),'tryexcept'執行到目前爲止最糟糕的*。 – musiphil

+0

確實如此,但是Python有這個原因的EFAP原則: 「請求原諒比權限(EFAP)更容易」。 http://www.python-academy.com/courses/specialtopics/python_patterns.html 這是真的:它 –

+0

沒錯,但Python有這個原因,EFAP原則: 「它更容易請求原諒比許可(EFAP)」。 http://www.python-academy.com/courses/specialtopics/python_patterns.html 確實如此:如果您必須除了所有內容之外,速度會更慢,但是您正在編寫代碼,希望它能夠正常工作。 嘗試-1(不存在)的時間爲: infunc - 82.3 ns,getvalue - 136 ns,tryexcept,630 ns。 擺脫選項2的原因很明顯......當關鍵時間達到90%時,我們獲得了類似的表現。 –

1

第一個和第三個選項都對我有意義。我更喜歡保留嘗試/除了實際例外的情況,而不是預期的結果(例如用戶錯誤)。如果標誌沒有很多時間,那麼我個人更喜歡情況1.如果標誌不是沒有代碼要執行,也可以做相反的處理。在那裏,即:

def foo(self, some_other_args): 
    ... do something ... 
    if 'target_names' not in self.task.flags: 
     # Nothing further to do here, so return the result 
     return result 

    ... do something with self.task.flags['target_names'] ... 
    return result 

但是我知道一些人喜歡避免這樣的多個回報。如果你真的認爲'target_names'出現在字典中,我認爲你的案例3可能是更好的解決方案,因爲它意圖清晰(缺少的關鍵是真正的例外,而不是標準案例)。

1

第三種方式,因爲你說它符合EAFP。該試/ except子句可以在此情況下(Python的3.4+)更succintly書面

注:

import contextlib 
with contextlib.suppress(KeyError): 
    foo(self.task.flags['target_names']) 
相關問題