2010-04-07 67 views
10

我正在嘗試創建一個在集合操作中行爲正常的自定義對象。集合操作中對象的行爲

我已經普遍認識到它的工作原理,但是我想確保我完全理解它的含義。特別是,當對象中有其他數據未包含在equal/hash方法中時,我對這種行爲感興趣。看起來,在「交集」操作中,它返回被比較的對象集合,其中'聯合'操作返回被比較的對象集合。

舉例說明:

class MyObject: 
    def __init__(self,value,meta): 
     self.value = value 
     self.meta = meta 
    def __eq__(self,other): 
     return self.value == other.value 
    def __hash__(self): 
     return hash(self.value) 

a = MyObject('1','left') 
b = MyObject('1','right') 
c = MyObject('2','left') 
d = MyObject('2','right') 
e = MyObject('3','left') 
print a == b # True 
print a == C# False 

for i in set([a,c,e]).intersection(set([b,d])): 
    print "%s %s" % (i.value,i.meta) 
#returns: 
#1 right 
#2 right 

for i in set([a,c,e]).union(set([b,d])): 
    print "%s %s" % (i.value,i.meta) 
#returns: 
#1 left 
#3 left 
#2 left 

這種行爲的地方,並確定記錄?如果是這樣,那麼治理原則是什麼?

回答

4

不,它不是確定性的。問題在於你已經破壞了equals和hash的不變量,當它們相等時,兩個對象是等價的。修復你的對象,不要試圖聰明,濫用set的實現工作。如果元值是MyObject身份的一部分,它應該包含在eq和hash中。

你不能依賴集合的交集來遵循任何順序,所以沒有辦法輕鬆地做你想要的。你最終會做的是隻取值的交集,然後通過所有的對象查看一個較舊的對象,以替換它,爲每一個。算法上沒有很好的方法。

工會都沒有那麼糟糕:

##fix the eq and hash to work correctly 
class MyObject: 
    def __init__(self,value,meta): 
     self.value = value 
     self.meta = meta 
    def __eq__(self,other): 
     return self.value, self.meta == other.value, other.meta 
    def __hash__(self): 
     return hash((self.value, self.meta)) 
    def __repr__(self): 
     return "%s %s" % (self.value,self.meta) 

a = MyObject('1','left') 
b = MyObject('1','right') 
c = MyObject('2','left') 
d = MyObject('2','right') 
e = MyObject('3','left') 

union = set([a,c,e]).union(set([b,d])) 
print union 
#set([2 left, 2 right, 1 left, 3 left, 1 right]) 

##sort the objects, so that older objs come before the newer equivalents 
sl = sorted(union, key= lambda x: (x.value, x.meta)) 
print sl 
#[1 left, 1 right, 2 left, 2 right, 3 left] 
import itertools 
##group the objects by value, groupby needs the objs to be in order to do this 
filtered = itertools.groupby(sl, lambda x: x.value) 
##make a list of the oldest (first in group) 
oldest = [ next(group) for key, group in filtered] 
print oldest 
#[1 left, 2 left, 3 left] 
+0

望着文檔的__hash__方法,它不會出現,表明不可能有未散列對象中的數據。我可以想到許多例子,其中兩個相同的對象將具有不同的某種形式的元數據(可能是時間戳或文件名)。 從__hash__的文檔:唯一需要的屬性是比較相等的對象具有相同的散列值;建議以某種方式混合在一起(例如,使用排他或散列值)用於對象的組成部分,這些組成部分也是對象比較的一部分。 – 2010-04-07 19:10:33

+1

我對你的評論感到困惑,你似乎同意我的看法。如果一個對象具有被eq和hash忽略的元數據(例如時間戳或文件名),那麼它們不夠重要,不能被保存,要麼被比較的對象會做。如果它們足夠重要以區分這兩個對象,它們將被包含在散列和等式中。你現在在問什麼? – hlfrk414 2010-04-07 19:22:33

+0

我並不贊同你;)。試圖瞭解如何使用這些功能。在這種情況下,我有一個由監視代理創建的對象。嘗試關聯具有不同時間戳的重複性警報條件。寧願保留舊的對象,但當然我可以用其他方式實現它,因爲我懷疑你是正確的。 – 2010-04-07 19:46:40

1

訂單似乎並不重要:

>>> [ (u.value, u.meta) for u in set([b,d]).intersection(set([a,c,e])) ] 
[('1', 'right'), ('2', 'right')] 

>>> [ (u.value, u.meta) for u in set([a,c,e]).intersection(set([b,d])) ] 
[('1', 'right'), ('2', 'right')] 

但是,如果你這樣做:

>>> f = MyObject('3', 'right') 

並添加f到「右」集:

>>> [ (u.value, u.meta) for u in set([a,c,e]).intersection(set([b,d,f])) ] 
[('1', 'right'), ('3', 'right'), ('2', 'right')] 

>>> [ (u.value, u.meta) for u in set([b,d,f]).intersection(set([a,c,e])) ] 
[('1', 'left'), ('3', 'left'), ('2', 'left')] 

所以你可以看到,行爲取決於集合的大小(如果你union發生相同的效果)。它也可能依賴於其他因素。如果你想知道爲什麼,我認爲你正在通過python源文件搜索。

0

比方說,你的對象有兩種不同類型的屬性:關鍵屬性和數據屬性。在你的例子中,MyObject.value屬性。

將所有對象存儲爲字典中的值,並以屬性爲關鍵字,確保只有您的首選(例如具有最早的時間戳)輸入字典中。與在詞典中使用相同的密鑰進行您所設定的操作,和檢索字典中的實際對象:

result= [dict1[k] for k in set_operation_result] 
相關問題