2013-05-10 103 views
7

我有一些Python類,如果簡化的,看起來像:Python - 類型檢查確定何時不會拋出錯誤?

class base: 
    def __init__(self, v): 
     self.value = v 

    def doThings(self): 
     print "Doing things." 

    def doMoreThings(self): 
     print "Doing more things." 

    def combine(self, b): 
     self.value += b.value 

class foo(base): 
    def showValue(self): 
     print "foo value is %d." % self.value 

class bar(base): 
    def showValue(self): 
     print "bar value is %d." % self.value 

base類包含實現兩者共同的foobar子類的功能的方法(由doThingsdoMoreThings以上表示)。 foobar子類主要區別在於它們如何解釋value字段。 (以上,它們只是在打印時顯示的內容不同,但在我的實際應用中,它們做了其他一些更復雜的事情。)base可以被認爲是「抽象的」:用戶只能使用foo s和bar秒。 base只存在於其子類通用的代碼中。

我想問的方法是combine,它可以讓你拿兩個這樣的對象,再做第三個。由於foobar解釋value不同,它沒有任何意義的不同類型的combine兩個子類:可以 s到獲得foo或兩個bar s到獲得bar但不是foobar。即使如此,combine的程序對於所有子類都是相同的,所以將它分解並定義在一個地方是有意義的。

如果用戶試圖combine兩個不兼容的對象,我可能會想要發出一個錯誤信號,但我沒有看到一種方法來做到這一點,而不會引入醜陋的類型檢測。這是否是一個好習慣?或者我應該做平常的事情,而不是檢查,記錄問題,並假定用戶不會嘗試以非意圖的方式使用combine,儘管這樣的使用似乎「成功」並返回垃圾對象而不是提出錯誤?

謝謝你的幫助。

回答

3

我在這裏看到的幾種方法:

  1. 不檢查任何與信任用戶做正確的事。根據情況可能是適當的或危險的。

  2. 檢查類型。你說得對,它看起來很醜,但卻是最簡單的。

  3. 沒有value,但將它們命名爲他們想要的方式(pressure,temperature)並讓它們自己組合。

  4. 與3相同,但另外還有一個子類property,它將對.value的訪問映射到它們各自的「實際」值變量。這樣,你可以保持。 __init__(),但是.combine()必須由每個子類完成。

  5. 與4相同,但不使用property,而是使用自制的descriptor。在this answer中,我展示了它是如何完成的。

2

改變結合方法

def combine(self, b): 
    if (not(self.__class__.__name__ == b.__class__.__name__)): 
     raise TypeError("%s cannot combine with %s" % (self.__class__.__name__, b.__class__.__name__)) 
    else:   
     self.value += b.value 

可替代地,改變爲基類定義

class base(object): 

,然後更簡單的,更好的,更快樂結合的方法是可能的(感謝glglgl)

def combine(self, b): 
    if (not(type(self) == type(b))): 
     raise TypeError("%s cannot combine with %s" % 
         (type(self), type(b))) 
    else:   
     self.value += b.value 
+0

比較'__class ______ name__'是恕我直言很醜陋。更好的方法是比較'type(self)== type(b)'或者使用'isinstance(b,type(self))''。 – glglgl 2013-05-10 07:38:34

+0

@glglgl''type(self)== type(b)''總是爲true,因爲它們都是''instance''。 ''isinstance''也有同樣的問題。同意它的醜陋,但... – Vorsprung 2013-05-10 07:46:15

+0

哎唷!沒有看到OP使用舊式課程......那麼很明顯。 (我儘量避免使用它們。)但是使用'isinstance'時,它應該也適用於舊式的類(剛測試過)。 – glglgl 2013-05-10 08:29:25

0

當我w在生產代碼上簽字,不應該失敗,我使用了一個裝飾器來確保冒險功能不會發生爆炸,變成火球並炸燬我的整個系統。

def bulltproof(func): 
    def _bulletproof(*args, **kwargs): 
     try: 
      result = func(*args, **kwargs) 
     except Exception as exc: 
      raise BulletproofError(exc) 
     return result 
    return _bulletproof 

使用它這樣的:

@bulletproof 
def some_risky_function(): 
    #.... 

,趕上只有一個錯誤:

try: 
    some_risky_function() 
except Bulletproof: 
... 

反而提高了,你可以返回None錯誤,記錄錯誤或關閉它測試。當然,當你想確保你的程序能夠從錯誤中恢復時,這種方法很有用,但有時最好死於繼續錯誤。

0

由於.combine()在概念上做了完全不同的事情,具體取決於對象是foo還是bar,我認爲它不是在抽象父類中具有該函數,而是將其保留在派生類中。即使它是相同的代碼,將foo的兩個實例組合起來也不同於組合bar的兩個實例的操作。如果.combine()的代碼實際上很長,您可以在基類中創建一個私有方法來完成這項工作,但只能從派生類中定義的函數中調用它。