2013-04-30 104 views
3

我正在使用Python的內置集合來保存我定義的類的對象。對於這門課,我定義了__eq____ne____hash__,以便我可以通過自定義比較函數比較對象。這工作得很好,直到我發現我實際上需要兩個套比較函數,這些函數將在我的代碼的不同時間以不同的方式使用。Python中內置類型的自定義比較函數

我無法在我的類中定義兩組__eq__等方法,並且Python的內置集合類型不接受比較器參數。我想我可以在集合周圍寫一個包裝類,但是這看起來好像比需要更多的工作。

有沒有比編寫我自己的集合類更容易的解決方案?

+2

您不能以僅等於某些時間的集合存儲對象。就集合而言,它們要麼是平等的,要麼是不平等的。 – 2013-04-30 18:46:09

+0

通常的解決方法是「裝飾」列表中的元素:根據備用比較元素存儲相同的包裝元素。這在評論中有點難以解釋,但如果聽起來很有希望,我可以寫出一個答案。 – abarnert 2013-04-30 18:48:55

+0

您可以在自己的類中編寫一個包裝器,而不是圍繞集編寫包裝器,該包裝器提供了替代等式定義。然後當你需要這個其他的等式定義時,有一組原始類的對象和另一組這些「替代」對象。 – BrenBarn 2013-04-30 18:49:20

回答

9

比方說,你有這個類:

class Thingy(object): 
    def __init__(self, key, notkey): 
     self.key, self.notkey = key, notkey 
    def __eq__(self, other): 
     return self.key == other.key 
    def __hash__(self): 
     return hash(self.key) 

現在,你想要把這些在一組,但notkey,而不是key鍵。你不能這樣做,因爲一個集合期望它的元素對於平等具有一致的含義,並且對於散列也是一致的,因此a == b總是暗示hash(a) == hash(b)。因此,創建一個包裝:

class WrappedThingy(object): 
    def __init__(self, thingy): 
     self.thingy = thingy 
    def __eq__(self, other): 
     return self.thingy.notkey == other.thingy.notkey 
    def __hash__(self): 
     return hash(self.thingy.notkey) 

你可以把那些一組:

wts = set(WrappedThingy(thingy) for thingy in thingies) 

例如,假設你想uniquify你一樣的東西,保持正好一個啄(任意)爲每個notkey值。只是包裝他們,粘在包裝中的一組,然後解開他們和粘unwrappees在一個列表:

wts = set(WrappedThingy(thingy) for thingy in thingies) 
thingies = [wt.thingy for wt in wts] 

這就是所謂的「DSU」更一般的蟒紋的一部分。這意味着「裝飾 - 排序 - 未裝飾」,這在當今是非常不準確的,因爲在現代Python中你幾乎從不需要它來進行與排序相關的任務......但從歷史上看,這是有道理的。隨意稱它爲「裝飾 - 過程 - 不裝飾」,希望它能夠迎頭趕上,但不要太難。

您現在不需要DSU進行排序的原因是大多數排序功能都以key函數作爲參數。實際上,即使是單獨使用,itertools recipes中的unique_everseen函數也需要key

但是,如果你看一下在幕後做什麼的,它基本上是DSU:

for element in iterable: 
    k = key(element) 
    if k not in seen: 
     seen.add(k) 
     yield element 

(這是一個發電機,而不是一個列表的建築功能,這意味着它可以「飛去除裝飾」 ,這使得事情變得簡單一些,但其他方面也是如此)