Python中内置类型的自定义比较函数



我正在使用Python的内置集合来保存我定义的类的对象。对于这个类,我定义了__eq____ne____hash__,以便可以通过自定义比较函数比较对象。这工作得很好,直到我发现我实际上需要两组比较函数,它们将在我的代码中的不同时间以不同的方式使用。

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

有没有比自己写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。不能按原样这样做,因为集合希望它的元素具有相等的一致含义,同时也具有散列的一致含义,这样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"的一部分。这代表"装饰-排序-不装饰",这在今天是非常不准确的,因为在现代Python中,你几乎不需要它来完成与排序相关的任务……但从历史上看,它是有意义的。你可以随意称呼它为"装饰-处理-装饰",希望它能流行起来,但不要抱太大希望。

现在不需要DSU进行排序的原因是大多数排序函数都将key函数作为参数。实际上,即使是为了惟一化,itertools食谱中的unique_everseen函数也使用key

但是如果你看一下它在封面下面做什么,它基本上是DSU:

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

(事实上,它是一个生成器而不是一个列表构建函数,这意味着它可以"动态地取消装饰",这使得事情变得简单一些。但除此之外,还是一样的。)

最新更新