2013-10-03 15 views
3

在Python中使用class作爲特殊值是否難看?使用class作爲特殊值?

考慮一下:

def find_result(): 
    result = None 
    # do something to find the result, even recursing 
    return result 

r = find_result() 
if r is None: 
    raise Exception("we have no result") 

這工作完全,如果我希望結果是數字,或任何「正常」的類型。

但是如果有一個任意的數據結構,結果可以是從None到另一個結構的任何東西?我在我的情況確實是這樣的:

class NoResult: 
    """i'm even less than `None`.""" 
    pass 

def query(data, path): 
    result = NoResult 
    # traverse and recurse into the data structure 
    return result 

r = query() 
if r is NoResult: 
    raise Exception("our hands are empty") 

它的工作原理,但我不能動搖,我是一個有點濫用這裏的貧困階層,感覺和甚至有可能是一個真正的危險潛伏內。

這是真的嗎?我濫用課堂嗎?或者只是我的算法很糟糕,如果它需要依賴像這樣的「特殊None」?

+2

爲什麼不在'find_result'中引發異常而不是返回任何東西?引發異常的整個想法是提供一種獨立於識別特殊返回值的替代錯誤檢測機制。 – chepner

+0

@chepner我是這樣做的,因爲我需要更深入地遞歸,並且我從來不知道我想停止搜索直到回到頂部的電話。現在我想到了,也許通過適當的異常處理,它可能會更好...... –

回答

5

它被稱爲哨兵,你可以使用任何唯一的對象吧:

sentinel = object() 

if r is sentinel: 
    raise Exception(..) 

這裏我用一個簡單的object()實例,而不是一個自定義類。

自定義類具有的優點是它可能更自我記錄;如果您的API 已將傳遞給API的用戶(可以是代碼),那麼顯式類更好。只需在API的黑盒子中使用,object()就好了。

你可能想考慮在你現在返回標記的地方引發異常。您可以隨時再次遇到異常。

+0

@AloisMahdal:是的;我可以看到那令人困惑。稍微改變了句子。 –

+0

我剛剛玩過我的舊代碼,發現直接使用該類的另一個優點(即它不是一個實例或一個通用對象):當您打印它時,它是'mymodule.NoResult'而不是'(Python 2.7)。它是完全免費的(不需要'__str__',只需'class X:pass'),並且可讀性更強,特別是如果你想使用多個哨兵。因此,即使您不打算打印它,它仍然適用於調試,並且在它偷偷摸摸的情況下,至少它可以將用戶/您發送到正確的位置。 –

+0

這就是我提到的'自我記錄'部分。 :-) –

2

僅僅爲了Martijn的建議添加另一種方法就是使用異常機制讓find_result首先拋出「No Result」異常,然後讓調用者決定如何處理它。

def find_result(): 
    result = None 
    if (...): 
     raise NoResultException("details") 
    return result 

try: 
    r = find_result() 
except NoResultException: 
    raise Exception("we have no result")