2013-03-15 35 views
2

從Python新手這裏請原諒無法使用Python的風格。實現兩個構造函數的最pythonic方法

我有一個類需要一個參數來建立初始數據。初始數據如何進入的方式有兩種:字符串列表或帶字符串鍵和整數值的字典。

現在,我只實現構造函數的一個版本,即以{}爲默認值的參數字典。該列表參數初始化被實現爲方法,即

myClass = MyClass() 
myClass.initList(listVar) 

我可以肯定這住,但是這肯定是不完美的。所以,我決定在這裏尋求一些Pythonic智慧:如何實現這樣的多態構造函數?我是否應該嘗試並且無法讀取initData.keys()以便嗅探這是否是字典?或者,也許嗅探參數類型來實現糟糕的多態性,在設計中不受歡迎的地方被認爲是非pythonic?

+1

爲什麼不使用'* args'和'** kwargs'? – 2013-03-15 13:59:21

+0

用戶類方法作爲備用構造函數。看到我這個類似的問題:http://stackoverflow.com/questions/682504/what-is-a-clean-pythonic-way-to-have-multiple-constructors-in-python/682545#682545 – Ber 2013-03-15 14:32:01

+2

另外,不要在功能或方法中使用「{}」作爲默認參數。爲了說明它在一些情況下,它表現爲一個「C靜態」變量 - 每個函數調用將看到與先前調用相同的對象。 – jsbueno 2013-03-15 14:37:30

回答

2

As @ Jan-PhilipGehrcke指出,pythonic可能難以量化。對我來說,它意味着:

  • 容易閱讀
  • 易於維護
  • 簡單比複雜好是比複雜
  • 等等,等等,等等(見Zen of Python完整列表更好,在解釋器中輸入import this

所以,最pythonic的解決方案取決於你必須爲每個支持的初始化器做什麼,以及它們中有多少喲你有。我會說,如果你有隻有極少數,而且每一個可以只按幾行代碼進行處理,然後用isinstance__init__

class MyClass(object): 

    def __init__(self, initializer): 
     """ 
     initialize internal data structures with 'initializer' 
     """ 
     if isinstance(initializer, dict): 
      for k, v in itit_dict.items(): 
       # do something with k & v 
       setattr(self, k, v) 
     elif isinstance(initializer, (list, tuple)): 
      for item in initializer: 
       setattr(self, item, None) 

在另一方面,如果你有很多可能的初始化,或如果其中任何一個需要大量的代碼來處理,那麼你會想爲每一個可能的init類型的一個classmethod構造,在__init__最常見的用法是:

class MyClass(object): 

    def __init__(self, init_dict={}): 
     """ 
     initialize internal data structures with 'init_dict' 
     """ 
     for k, v in itit_dict.items(): 
      # do something with k & v 
      setattr(self, k, v) 

    @classmethod 
    def from_sequence(cls, init_list): 
     """ 
     initialize internal data structures with 'init_list' 
     """ 
     result = cls() 
     for item in init_list: 
      setattr(result, item, None) 
     return result 

這樣可以使每一個可能的構造簡單,乾淨,易於理解。作爲一個方面說明:使用可變對象作爲默認值(就像我在上面的__init__中所做的那樣)需要小心完成;原因是默認值只被評估一次,然後無論結果是什麼,將被用於之後的每個調用。當你在你的函數中修改這個對象時,這只是一個問題,因爲這些修改會在每次後續調用中看到 - 除非你想創建一個可能不是你想要的行爲的緩存。這對我的例子來說不是問題,因爲我沒有修改init_dict,只是迭代它(如果調用者沒有替換它,因爲它是空的,這是沒有操作的)。

+1

這會中斷,因爲調用沒有參數的「cls」不會將強制性的'init_dict'傳遞到'__init__' – jsbueno 2013-03-15 14:34:28

+0

@jsbueno:謝謝,修正。 – 2013-03-15 14:38:52

+0

讓我們不要爲「最pythonic解決方案」的戰爭而戰。這很難量化。你介意從你的答案中刪除這部分嗎? – 2013-03-15 15:31:10

2

Python中沒有函數overloading。在__init__()方法使用if isinstance(...)將是非常簡單的閱讀和理解:

class Foo(object): 
    def __init__(self, arg): 
     if isinstance(arg, dict): 
      ... 
     elif isinstance(arg, list): 
      ... 
     else: 
      raise Exception("big bang") 
+0

這將工作。但是,如果我完全誠實,這不會讓我感覺到Pythonic。 – NPE 2013-03-15 15:42:10

+0

我不喜歡「Pythonic」這個詞。不過,我很高興看到你想要表達的實際觀點:-) - 那麼,你的具體意思是什麼? (這裏沒有諷刺意味,我非常贊同你的意見)。 – 2013-03-15 15:47:21

+0

簡而言之,它非常易讀;當你的代碼開始變得更長時,因爲每個可能的情況都需要大量的處理,不再容易閱讀或維護 - 那麼'classmethod's是你最好的選擇。 – 2013-03-15 16:36:59

-1

您可以使用*args**kwargs做到這一點。 但是如果你想知道什麼類型的參數是你應該使用type()isinstance()

+0

你可以添加一個例子嗎?我不確定你是什麼意思。 – 2013-03-15 16:37:58

3

在一個理想的世界裏,你會寫一個構造函數,它可以採用listdict而不知道區別(即鴨子類型)。當然,這是不太現實的,因爲這些都是非常不同的鴨子。

可以理解的是,對於檢查實際實例類型的想法也有一點烙印,因爲它打破了鴨子打字的想法。但是,在Python 2.6中引入了一個名爲abc的有趣模塊,它允許定義「抽象基類」。要被視爲一個抽象基類的實例,實際上並不需要從它繼承,而只需要實現它的所有抽象方法。

collections模塊包含一些抽象的基類,這些基類在這裏很有用,即collections.Sequencecollections.Mapping。因此,你可以寫你的__init__功能,如:

def __init__(self, somedata): 
    if isinstance(somedata, collections.Sequence): 
     # somedata is a list or some other Sequence 
    elif isinstance(somedata, collections.Mapping): 
     # somedata is a dict or some other Mapping 

http://docs.python.org/2/library/collections.html#collections-abstract-base-classes包含的由每個ABC提供的方法的細節。如果你堅持這些,那麼你的代碼現在可以接受符合這些抽象基類之一的任何對象。而且,只要服用內置dictlist類型,你可以看到:

>>> isinstance([], collections.Sequence) 
True 
>>> isinstance([], collections.Mapping) 
False 
>>> isinstance({}, collections.Sequence) 
False 
>>> isinstance({}, collections.Mapping) 
True 

而且,幾乎是在偶然,你只是做它tuple工作了。你可能不在乎它是否真的是list,只是你可以從中讀出元素。但是,如果您檢查了isinstance(somedata, list),您將排除tuple。這就是使用ABC購買你的東西。

+0

「*您對檢查實際實例類型的想法有點沮喪,因爲它打破了鴨子打字的想法*」 - 您的方法非常整齊,我喜歡它 - 我只想指出它仍以廣義的方式檢查實例類型。我經常閱讀它,但我仍然不明白爲什麼'isinstance()'應該是一件壞事。 – 2013-03-15 15:51:04

+0

@ Jan-PhilipGehrcke:是的,這是一個很好的觀點。這更像是檢查一個「鴨類」而不是「特定的鴨子家族」。至於'isinstance()'不好 - 我認爲它也不好。只是使用它可以很容易地使你的代碼比它必須處理的對象更具限制性。 – FatalError 2013-03-15 16:14:22

+0

謝謝@FatalError指向這個ABC模塊!在這個特定的項目中,我有興趣讓這些類型保持相當的限制。但很高興知道有這樣一個整潔的工具可以使鴨誘餌適合於任務,而不是抱怨它沒有鴨DNA。 – Passiday 2013-03-15 22:28:03

相關問題