我想使用一个集合来管理“ myItem”实例的集合。 myItem类具有自己的哈希函数。 这些项目的散列是基于每个项目中的部分而非全部数据,为简单起见,在下面的示例中,“数据”为字典r。哈希考虑了两个密钥hk1和hk2,并且哈希计算中没有考虑第三个密钥“ sad”。
class myItem():
def __init__(self, r):
# r is a dict holding information about the instance
# of course r has to have certain keys...
self.r = r
def __hash__(self):
"""Override the default hash behavior"""
return hash(tuple(sorted([self.r['hk1'],self.r['hk2']])))
def __eq__(self,other):
"""checking equality"""
if isinstance(other, self.__class__):
return self.__hash__() == other.__hash__()
return NotImplemented
def __ne__(self, other):
"""checking inequality"""
if isinstance(other, self.__class__):
return not self.__eq__(other)
return NotImplemented
def __repr__(self):
return str(self.r)
预期的行为通过下面的简短单元测试得到确认。
class testMySet(unittest.TestCase):
def testMyItemstuff(self):
m1 = myItem({'hk1':'val1', 'hk2': 100, 'sad': 'other stuff'})
m2 = myItem({'hk1': 'val1', 'hk2': 100, 'sad': 'different other stuff'})
self.assertEqual(m1, m2)
self.assertNotEqual(m1.r['sad'], m2.r['sad'])
s = { m1 }
# add m2 to s
s.add(m2)
# same hash, m2 is not added
self.assertEqual(len(s), 1)
# set contains the original object, not the last one added
self.assertNotEqual(s.pop().r['sad'], 'different other stuff')
我的问题是,我该如何修改行为,以使其哈希与现有对象重合的新对象最终替换原始对象,而对性能的影响却最小?
答案 0 :(得分:0)
您可以实现自定义的set
派生形式:
class CustomSet(set):
def add(self, item):
self.discard(item)
super().add(item)
请注意,这依赖于以下事实:如您的示例所示,当且仅当它们的哈希值比较相等时,两个项目才相等。
但这不是打算使用内置的基于散列的容器的方式。他们使用哈希进行快速查找,并在发生冲突的情况下使用相等性比较来解决冲突(即检查其是否为真正的相等性)。
如果__eq__
依赖于哈希以外的其他内容,那么您还需要跟踪哈希(例如,以dict
的形式):
class CustomSet(set):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._hashes = {}
def add(self, item):
self.discard(self._hashes.get(hash(item)))
self._hashes[hash(item)] = item
super().add(item)
# Similarly implement the following methods to update self._hashes:
# * clear
# * discard
# * pop
# * remove
答案 1 :(得分:0)
以这种方式定义哈希是否对您的应用程序有意义,这确实由您决定,但这似乎不太可能。
无论如何,我可以想到两个将与集合一样快的选项-O(1)而不是O(n)-并且它们的速度取决于实现您所描述的哈希函数:
首先,整理您的课程并创建实例:
class Item():
def __init__(self, a, b):
self.a = a
self.b = b
def __hash__(self):
return hash(self.a)
def __eq__(self,other):
if isinstance(other, self.__class__):
# Ignoring .b attribute
return self.a == other.a
else:
return NotImplemented
def __repr__(self):
return "Item(%s, %s)" % (self.a, self.b)
i1 = Item(1,2)
i2 = Item(3,4)
i3 = Item(1,5)
print(i1 == i2) # False (.a's don't match)
print(i1 == i3) # True (.a's match)
方法1:字典值
# Using a dict
updating_set = {}
updating_set[i1] = i1 # .values(): [Item(1, 2)]
updating_set[i2] = i2 # .values(): [Item(1, 2), Item(3, 4)]
updating_set[i3] = i3 # .values(): [Item(1, 5), Item(3, 4)]
print(list(updating_set.values()))
# [Item(1, 5), Item(3, 4)]
方法2:使用设置的子类
# Using a set subclass
class UpdatingSet(set):
def add(self, item):
if item in self: super().remove(item)
super().add(item)
uset = UpdatingSet()
uset.add(i1) # UpdatingSet({Item(1, 2)})
uset.add(i2) # UpdatingSet({Item(1, 2), Item(3, 4)})
uset.add(i3) # UpdatingSet({Item(1, 5), Item(3, 4)})
奖励方法3:不需要特殊的哈希函数
class NewItem():
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return "Item(%s, %s)" % (self.a, self.b)
class ItemSet():
def __init__(self):
self.items = {}
def add(self, item):
item_hash = item.a
self.items[item_hash] = item
def values(self):
return self.items.values()
i1 = NewItem(1,2)
i2 = NewItem(3,4)
i3 = NewItem(1,5)
iset = ItemSet()
iset.add(i1) # .values(): [Item(1, 2)]
iset.add(i2) # .values(): [Item(1, 2), Item(3, 4)]
iset.add(i3) # .values(): [Item(1, 5), Item(3, 4)]
print(list(iset.values())) # [Item(1, 5), Item(3, 4)]
第三种方法不需要您实现哈希(这可能会导致意外的副作用,但是可以使用“哈希函数”作为字典来模拟ItemSet.add()
内部的哈希过程。键。
这可能是您最好的选择,除非您真的想要实现哈希并知道该决定的影响程度。