集合操作中对象的行为

时间:2010-04-07 18:28:15

标签: python object set

我正在尝试创建一个在设置操作中正常运行的自定义对象。

我一般都认为它有效,但我想确保我完全理解其含义。特别是,当对象中存在未包含在等于/哈希方法中的其他数据时,我对此行为感兴趣。似乎在'intersection'操作中,它返回正在进行比较的对象集,其中'union'操作返回正在比较的对象集。

举例说明:

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

此行为是否记录在某处且具有确定性?如果是这样,那么治理原则是什么?

3 个答案:

答案 0 :(得分: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]

答案 1 :(得分: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源码。

答案 2 :(得分:0)

假设您的对象有两种不同类型的属性: key 属性和 data 属性。在您的示例中,MyObject.value属性。

将所有对象存储为字典中的值,并按属性键入,确保仅在字典中输入您的首选对象(例如,使用最旧的时间戳)。使用字典中使用的相同键执行集合操作,并从字典中检索实际对象:

result= [dict1[k] for k in set_operation_result]