Python中内置类型的自定义比较功能 [英] Custom comparison functions for built-in types in Python
问题描述
我正在使用Python的内置集来保存我定义的类的对象.对于此类,我定义了__eq__
,__ne__
和__hash__
,以便可以通过自定义比较函数比较对象.效果很好,直到我发现我实际上需要两个比较函数集,这些比较函数将在我的代码中的不同时间以不同的方式使用.
I am using Python's built-in sets to hold objects of a class I have defined. For this class, I defined __eq__
, __ne__
, and __hash__
so that I can compare objects by my custom comparison functions. That works just fine, until I find out that I actually need two sets of comparison functions, which will be used in different ways at different times in my code.
我无法在类中定义两组__eq__
等方法,并且Python的内置set类型不接受比较器参数.我想我可以围绕set编写包装类,但这似乎比必要的工作还要多.
I can't define two sets of __eq__
, etc. methods in my class, and Python's built-in set type does not accept a comparator argument. I suppose I could go write a wrapper class around set, but that seems like a lot more work than necessary.
有没有比编写自己的set类更简单的解决方案了?
Is there any easier solution to this than writing my own set class?
推荐答案
假设您有此类:
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)
.因此,创建一个包装器:
Now, you want to put these in a set, but keyed by notkey
instead of key
. You can't do that as-is, because a set expects its elements to have a consistent meaning for equality—and also a consistent meaning for hash such that a == b
always implies hash(a) == hash(b)
. So, create a wrapper:
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)
您可以将那些放在一个集合中:
And you can put those in a set:
wts = set(WrappedThingy(thingy) for thingy in thingies)
例如,假设您要统一事物,为每个notkey
值精确地(任意)保持一个事物.只需包裹它们,将包裹器粘在一个集合中,然后将它们解开并将未包裹的东西粘在列表中:
For example, let's say you want to uniquify your thingies, keeping exactly one thingy (arbitrarily) for each notkey
value. Just wrap them, stick the the wrappers in a set, then unwrap them and stick the unwrappees in a list:
wts = set(WrappedThingy(thingy) for thingy in thingies)
thingies = [wt.thingy for wt in wts]
这是更通用的Python模式"DSU"的一部分.这代表"decorate-sort-undecorate",这在当今已经很不准确了,因为在现代Python中几乎不需要它来执行与排序相关的任务……但是从历史上看,它是有道理的.随意将其称为装饰过程无法装饰",希望它会流行起来,但不要太过努力.
This is part of a more general Python pattern called "DSU". This stands for "decorate-sort-undecorate", which is wildly inaccurate nowadays, since you almost never need it for sorting-related tasks in modern Python… but historically it made sense. Feel free to call it "decorate-process-undecorate" in hopes that it will catch on, but don't hope too hard.
现在不需要DSU进行排序的原因是,大多数排序函数都将key
函数用作参数.实际上,即使是唯一化,在 itertools
食谱中的unique_everseen
函数取key
.
The reason you don't need DSU for sorting nowadays is that most sorting functions all take key
functions as arguments. In fact, even for uniquifying, the unique_everseen
function in the itertools
recipes takes a key
.
但是,如果您仔细看一下它的功能,基本上就是DSU:
But if you look at what it does under the covers, it's basically DSU:
for element in iterable:
k = key(element)
if k not in seen:
seen.add(k)
yield element
(事实上,它是一个生成器,而不是一个列表构建函数,意味着它可以即时取消装饰",这使事情变得简单一些.但除此之外,还是一样的想法.)
(The fact that it's a generator rather than a list-building function means it can "undecorate on the fly", which makes things a bit simpler. But otherwise, same idea.)
这篇关于Python中内置类型的自定义比较功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!