有没有办法给set()
一个比较器,所以当添加项目时,它会检查该项目的属性是否相似而不是项目是否相同?例如,我想在集合中使用可以包含一个属性的相同值的对象。
class TestObj(object):
def __init__(self, value, *args, **kwargs):
self.value = value
super().__init__(*args, **kwargs)
values = set()
a = TestObj('a')
b = TestObj('b')
a2 = TestObj('a')
values.add(a) # Ok
values.add(b) # Ok
values.add(a2) # Not ok but still gets added
# Hypothetical code
values = set(lambda x, y: x.value != y.value)
values.add(a) # Ok
values.add(b) # Ok
values.add(a2) # Not added
我已经实现了我自己的类似功能,但想知道是否有内置方式。
from Queue import Queue
class UniqueByAttrQueue(Queue):
def __init__(self, attr, *args, **kwargs):
Queue.__init__(self, *args, **kwargs)
self.attr = attr
def _init(self, maxsize):
self.queue = set()
def _put(self, item):
# Potential race condition, worst case message gets put in twice
if hasattr(item, self.attr) and item not in self:
self.queue.add(item)
def __contains__(self, item):
item_attr = getattr(item, self.attr)
for x in self.queue:
x_attr = getattr(x, self.attr)
if x_attr == item_attr:
return True
return False
def _get(self):
return self.queue.pop()
答案 0 :(得分:2)
只需根据相关属性定义对象上的__hash__
和__eq__
,它就可以使用set
s。例如:
class TestObj(object):
def __init__(self, value, *args, **kwargs):
self.value = value
super().__init__(*args, **kwargs)
def __eq__(self, other):
return self.value == other.value
def __hash__(self):
return hash(self.value)
如果你不能改变对象(或者不想,因为其他事情对于平等很重要),那么请改用dict
。你可以这样做:
mydict[obj.value] = obj
所以新对象替换旧的,或
mydict.setdefault(obj.value, obj)
如果问题中的value
已经存在于密钥中,则会保留旧对象。只需确保使用.viewvalues()
(Python 2)或.values()
(Python 3)进行迭代,而不是直接迭代(这将获得键,而不是值)。您实际上可以使用这种方法来创建一个自定义的set
- 类似对象,如您所描述的那样(尽管您需要实现比我显示的方法更多的方法以使其有效,但默认方法通常相当慢):
from collections.abc import MutableSet # On Py2, collections without .abc
class keyedset(MutableSet):
def __init__(self, it=(), key=lambda x: x):
self.key = key
self.contents = {}
for x in it:
self.add(x)
def __contains__(self, x):
# Use anonymous object() as default so all arguments handled properly
sentinel = object()
getval = self.contents.get(self.key(x), sentinel)
return getval is not sentinel and getval == x
def __iter__(self):
return iter(self.contents.values()) # itervalues or viewvalues on Py2
def __len__(self):
return len(self.contents)
def add(self, x):
self.contents.setdefault(self.key(x), x)
def discard(self, x):
self.contents.pop(self.key(x), None)