2013-12-18 40 views
28

也許這是一個比技術問題更多的樣式問題,但我有一個帶有多個成員變量的Python類,我希望它的工作,以便一些成員變量當用戶第一次創建類的實例(即在__init__函數中)時初始化,並且我希望其他成員變量可以從將在稍後調用的成員函數的參數中定義。所以我的問題是我應該初始化__init__函數中的所有成員變量(並將稍後將定義的那些變量設置爲虛擬值),或初始化函數中的某些變量,並在後面的函數中初始化一些變量。我意識到這可能很難理解,所以這裏有幾個例子。Python - 應該在__init__中初始化所有成員變量

該示例首先在__init__函數中將var3設置爲0,然後在my_funct函數中稍後將其設置爲期望值。

class myClass(object): 
    def __init__(self,var1,var2): 
     self.var1=var1 
     self.var2=var2 
     self.var3=0 

    def my_funct(self,var3): 
     self.var3=var3 

,並在這個例子中,var3沒有在__init__功能在所有定義

class myClass(object): 
    def __init__(self,var1,var2): 
     self.var1=var1 
     self.var2=var2 

    def my_funct(self,var3): 
     self.var3=var3 

我不認爲無論哪種方式會帶來很大的區別(也許在內存使用情況略有差別) 。但我想知道是否由於某種原因,其中一種比另一種更受歡迎。

回答

19

在面向對象編程中,開發者需要確保在實例化和方法結束後對象始終處於一致狀態。除此之外,你可以隨心所欲地開發這個類(記住一些具有子類/覆蓋等的原則)。

當您在__init__之外設置實例變量時,Pylint等工具會發出警告。可以爭辯說,設置__init__中的所有實例變量都是更清晰的,但它並不是一個必須始終遵守的規則。

+1

同意。一致性是關鍵 - 您不希望外人使用您的API或類處於無效狀態。 –

+0

你能簡單地定義什麼'一致的狀態'的含義。這是否意味着在實例化之後不應該添加新的成員變量? – user1893354

+1

@ user1893354你可以在實例化之後添加變量,但不應該可以創建一個對象,調用一個或多個方法並最終得到一個雜亂的對象。方法的行爲及其返回值應始終保持一致。例如,我們不能有一個報告「破損」和「功能正常」的Car類。 –

3

我實際上會阻止將你不需要的變量初始化爲__init__到一個任意的默認值。

如果是這樣的話,我的確質疑您使用面向對象,但是我確定有一個有效且可以理解的情況,其中__init__不會做所有事情,並且該類需要通過添加其他屬性來進一步修改自己其他方法。

在我看來,測試運行可能想要使用它的方法時是否設置了變量的正確方法是使用hasattr。在這種情況下,這是使用該方法的有效方法,並且測試只是以合理的方式切換行爲。

另一種方法是嘗試使用它並處理異常,並提供一些關於您的類的用戶做錯的用戶友好信息。在這種情況下,該方法需要在運行之前設置屬性。

即嗨,您已經初始化了該類,但在運行z_run方法之前,您需要通過調用z_init方法確保z屬性存在。

另一種可以說是更pythonic的方式是僅僅記錄如何在文檔字符串中使用該方法,然後在異常使用時發生異常。這對第一次執行某些內容來說已經足夠了,然後您可以專注於下一個任務。這與上面的情況相同,該方法需要設置屬性。

我不喜歡將變量初始化爲任意默認值的原因是這可能會造成混淆(因爲它是任意的)並且是線路噪聲。

如果該值武斷和簡單,可以爲您應在__init__方法,其可覆蓋使用默認值來改變默認值。它實際上也可以是一個有效的初始狀態,這也是而不是任意,您應該在__init__方法中設置它。

所以,真正的答案是這取決於,你或許應該避免它,如果你要麼通過其他方法添加屬性或屬性初始化爲任意值這樣質疑你OO的使用。

儘管Simeon Visser說要讓你的對象保持一致的狀態,但他沒有基於你的抽象例子的一致性的基礎。雖然Pylint對這類事情發出警告,但是皮棉計劃發出的警告很簡單,因此高級審閱者可以注意到通常表示代碼異味。我說高級審稿人是因爲真正的審稿人應該閱讀並理解你的所有代碼,因此不需要Pylint。

,打破經驗​​法則的例子:

class Mutant(object): 
    """A mutant!""" 

    def __init__(self): 
     """A mutant is born with only 1 eye and 1 mouth""" 

     self.eyes = 1 
     self.mouth = 1 
     self.location = 'Montana' 

    def roll_to(self, location): 
     """If they have limbs, running is less dangerous""" 

     if hasattr(self, 'limbs'): 
      print 'Your mutant broke its limbs off!!' 
      del self.limbs 

     self.location = location 

    def run_to(self, location): 
     """If they don't have limbs, running is not effective""" 

     if not hasattr(self, 'limbs'): 
      print 'Your mutant tries to run but he has no limbs.' 
     else: 
      self.location = location 

    def grow_limbs(self, number_of_limbs): 
     """Ah, evolution!""" 

     assert number_of_limbs > 0, 'Cannot grow 0 or less limbs...' 

     if hasattr(self, 'limbs'): 
      self.limbs += number_of_limbs 
     else: 
      self.limbs = number_of_limbs 
+0

好點。我想到的情況是其中一個成員變量是您想要保存在對象中的成員函數的輸出。在這種情況下,變量只能在調用成員函數後用實際值進行實例化。 – user1893354

+0

所以,讓我知道我理解你正確...例如,'obj'有'output_complex_calculation',並且要'obj.set_result_of_complex_calculation(obj.output_of_complex_calculation())' –

+0

例如,如果你有一個該類是用戶在實例化時輸入模型參數的預測模型。然後用戶調用.predict()函數創建預測列表。但是,您希望將這些對象的預測保存爲成員變量。那麼也許其他一些成員函數會對這個新的預測成員變量做些什麼。因此,在這種情況下,預測成員變量的實際值只能在調用.predict()後實例化。 – user1893354