Python中内置类型的自定义比较功能


问题内容

我正在使用Python的内置集来保存我定义的类的对象。对于此类,我定义了__eq____ne__和,__hash__以便可以通过自定义比较功能比较对象。这工作得很好,直到我发现我确实需要
2 套对比功能,这将在不同的方式在不同的时间在我的代码中使用。

我无法__eq__在我的类中定义两组等方法,并且Python的内置设置类型不接受比较器参数。我想我可以围绕set编写包装类,但这似乎比必要的工作多得多。

有没有比编写自己的集合类更简单的解决方案了?


问题答案:

假设您有这个课程:

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。您不能按原样进行操作,因为set期望其元素具有相同的相等性含义-
以及对hash的一致含义(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)

例如,假设您要对自己的东西进行唯一化,为每个notkey值精确地(任意)保持一个东西。只需包装它们,将包装器粘在一个集合中,然后解开包装,然后将展开的对象粘在列表中:

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

这是更通用的Python模式“ DSU”的一部分。这代表“ decorate-sort-
undecorate”,这在当今已经非常不准确了,因为在现代Python中几乎不需要它来执行与排序相关的任务……但是从历史上看,它是有道理的。随意称其为“装饰过程无法装饰”,希望它能流行起来,但不要太过努力。

您现在不需要DSU进行排序的原因是,大多数排序函数都将key函数作为参数。实际上,即使是唯一化,配方中unique_everseen功能也需要一个。itertoolskey

但是,如果您仔细看一下它的功能,基本上就是DSU:

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

(事实上​​,它是一个生成器,而不是一个列表构建函数,这意味着它可以“即时取消装饰”,这使事情变得简单一些。但是除此之外,相同的想法。)