您可以将可变对象存储在哈希表中,但您可能不应该因为如果您有一个指向对象的指针,您就将该对象存储在哈希表中,你改变了对象,然后你尝试通过指针查找它,你(几乎可以肯定)不会找到它,因为哈希现在已经改变了。但是,如果您的特定用例是像这样删除重复项,那么最有效的方法将是哈希。如果您没有使用多线程并且这种重复删除是自包含的,那么您可以安全地使用哈希表(像您拥有的 dict 或 set),因为数据实际上是不可变的。在这种情况下,唯一的风险是添加一个通常没用的__hash__ 函数。一种选择是创建一个实现__hash__ 的瘦包装类,将数据包装在其中以删除重复项,然后将其解包,如下所示:
class Wrapper(object):
__init__(self, wrapped):
self.wrapped = wrapped
__eq__(self, other):
if not isinstance(other, Wrapper):
return False
return self.wrapped == other.wrapped
__hash__(self):
# custom logic here, e.g.
return 31 * sum(hash(item) for item in self.wrapped)
def without_duplicate_lists(lists):
wrappers = (Wrapper(l) for l in lists)
result = []
seen = set()
for w in wrappers:
if w not in seen:
seen.add(w)
result.append(w)
return [w.wrapped for w in wrappers]
还有一些其他方法可以在不计算哈希的情况下执行此操作,例如使用二叉搜索树(树图)。但是,哈希并不是在映射中存储可变数据的固有问题,而是如果您以任何方式更改键,您就失去了价值。对于散列表,关键是散列。对于二叉搜索树,排序是由键本身定义的。如果密钥本身发生变化,无论使用什么方法都无法查找。
另外,请注意,在我的示例中,计算哈希是一个 O(n) 操作,因此如果您要删除 lists 或 sets 或 dicts 的重复项,转换可能会更简洁它们(或它们的键)到 tuples 或 frozensets 是不可变的,可以在 set 中使用而无需担心(甚至适用于其他对象)。
如果由于某种原因存在中间立场,您认为通过哈希删除重复的可变数据是一个坏主意,但您仍然认为删除重复的可变数据没问题,那么下一个最有效的选择可能是对数据进行排序.这样,您可以在 O(n*log(n)) 时间内排序,然后迭代,而不是 O(n^2) 用于嵌套迭代的幼稚解决方案,只保留当前值不等于最后一个值。这将要求您实现__eq__、__gt__ 和__lt__(或后者之一并使用@total_ordering):
def without_duplicates_sorted(objs):
objs = sorted(objs)
last = objs[0]
result = [last]
for current in objs[1:]:
if current != last:
result.append(current)
last = current
return result
为了完整起见,天真的解决方案:
def without_duplicates_naive(objs):
result = []
for obj in objs:
if obj not in objs[i+1:]:
result.append(obj1)
return result