2013-04-30 67 views
3

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

我无法在我的类中定义两组__eq__等方法,并且Python的内置集合类型不接受比较器参数。我想我可以在集合周围写一个包装类,但是这看起来好像比需要更多的工作。

有没有比编写我自己的集合类更容易的解决方案?

+2

您不能以仅等于某些时间的集合存储对象。就集合而言,它们要么是平等的,要么是不平等的。 – 2013-04-30 18:46:09

+0

通常的解决方法是“装饰”列表中的元素:根据备用比较元素存储相同的包装元素。这在评论中有点难以解释,但如果听起来很有希望,我可以写出一个答案。 – abarnert 2013-04-30 18:48:55

+0

您可以在自己的类中编写一个包装器,而不是围绕集编写包装器,该包装器提供了替代等式定义。然后当你需要这个其他的等式定义时,有一组原始类的对象和另一组这些“替代”对象。 – BrenBarn 2013-04-30 18:49:20

回答

9

比方说,你有这个类:

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) 

例如,假设你想uniquify你一样的东西,保持正好一个啄(任意)为每个notkey值。只是包装他们,粘在包装中的一组,然后解开他们和粘unwrappees在一个列表:

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

这就是所谓的“DSU”更一般的蟒纹的一部分。这意味着“装饰 - 排序 - 未装饰”,这在当今是非常不准确的,因为在现代Python中你几乎从不需要它来进行与排序相关的任务......但从历史上看,这是有道理的。随意称它为“装饰 - 过程 - 不装饰”,希望它能够迎头赶上,但不要太难。

您现在不需要DSU进行排序的原因是大多数排序功能都以key函数作为参数。实际上,即使是单独使用,itertools recipes中的unique_everseen函数也需要key

但是,如果你看一下在幕后做什么的,它基本上是DSU:

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

(这是一个发电机,而不是一个列表的建筑功能,这意味着它可以“飞去除装饰” ,这使得事情变得简单一些,但其他方面也是如此)