Python中的IdentitySet?
我需要一个集合,它使用标识(Python中的IdentitySet?,python,python-2.7,Python,Python 2.7,我需要一个集合,它使用标识(is)比较来进行添加(),而不是值(=)比较 例如,我定义了一个点类(具有不可变的x/y),该类钩住了\uuuuuueq\uuuuu和\uuuuuhash\uuuu点s正确比较自身,具有相同x/y值的点的两个实例回答真到==和假到是。我需要将两个这样的实例添加到我的集合中,并且结果必须包含这两个实例,即使它们的x/y值相同。一些小型会谈为此目的定义了IdentitySet 例如,我想知道是否可以修补内置的set类,使其包含identityAdd和identityRem
is
)比较来进行添加()
,而不是值(=
)比较
例如,我定义了一个点
类(具有不可变的x/y),该类钩住了\uuuuuueq\uuuuu
和\uuuuuhash\uuuu
<代码>点s正确比较自身,具有相同x/y值的点
的两个实例回答真
到==
和假
到是
。我需要将两个这样的实例添加到我的集合中,并且结果必须包含这两个实例,即使它们的x/y值相同。一些小型会谈为此目的定义了IdentitySet
例如,我想知道是否可以修补内置的set类,使其包含identityAdd
和identityRemove
方法。这可能会奏效,但看起来像是一个黑客
有更好的方法吗?您可以将点对象包装成可以比较其身份的东西
class Ref(object):
def __init__(self, value):
self.value = value
def __eq__(self, other):
return self.value is other.value
def __hash__(self):
return id(self.value)
下面是一个IdentitySet
实现,它将元素封装在Ref
对象中:
from collections import MutableSet
class IdentitySet(MutableSet):
def __init__(self, items = []):
self.refs = set(map(Ref, items))
def __contains__(self, elem):
return Ref(elem) in self.refs
def __iter__(self):
return (ref.value for ref in self.refs)
def __len__(self):
return len(self.refs)
def add(self, elem):
self.refs.add(Ref(elem))
def discard(self, elem):
self.refs.discard(Ref(elem))
def __repr__(self):
return "%s(%s)" % (type(self).__name__, list(self))
我不知道这是否是最好的方法,但是使用id=>object的字典怎么样?i、 e:
def insert_object(obj):
id_dict[id(obj)] = obj
def contains_object(obj):
return id_dict.has_key(id(obj))
id
返回真正的标识(内存地址),因此比较它本质上等同于is
但是,真的有一个很好的理由为什么你不能仅仅更新你的eq/散列方法吗
编辑-这里有一个结合@tom的集合
子类方法的想法:
class IdentitySet(set):
def __init__(self, items = []):
set.__init__(self)
self._identitydict = {}
for item in items:
self.add(item)
def __contains__(self, elem):
return set.__contains__(self, id(elem))
def __iter__(self):
for identity in set.__iter__(self):
yield self._identitydict[identity]
def add(self, elem):
identity = id(elem)
set.add(self, identity)
self._identitydict[identity] = elem
def discard(self, elem):
if elem in self:
self.remove(elem)
def pop(self):
return self._identitydict.pop(set.pop(self))
def remove(self, elem):
identity = id(elem)
set.remove(self, identity)
del self._identitydict[identity]
集合包含ID,ID作为映射到对象的字典键进行复制。这基本上是相同的方法,除了@tom将输入包装在
Ref
中,并使用id
作为Ref
的散列,而我只是直接将输入包装在id
中。如果没有中间类,它可能会更有效。下面是一个基于id->object
映射(建议的)的实现,并且:
例如:
a = (1, 2)
print IdentitySet([a, (1, 2), a])
# -> IdentitySet([(1, 2), (1, 2)]) # only one instance of `a`
print s != Set([a, (1, 2), a]) # it might be unequal because new literal
# tuple (1, 2) might have a different id
print s | Set([a, (1, 2), a])
# -> IdentitySet([(1, 2), (1, 2), (1, 2)]) # `a` plus two tuples from literals
“代码>代码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>易码>,<代码>移除<<代码>易码>易码>易码>易码<<代码<<代码>代码<代码>易码<<代码<<代码>代码<代码<代码>u代码>u代码<<代码<代码<代码<代码<代码<代码<代码<代码>u代码<代码<代码<代码<代码<代码<代码<代码<代码<代码>u代码<代码>u代码>u代码<代码<代码<代码<代码<代码<代码<代码<代码<代码<代码<代码<代码<代码<代码<代码<代码<代码>u代码uuu,\uuuu ge\uuuu
,\uuuu和uuu
,\uuuu或uu
,\uuuuu子
,\uuuuuuxor\uuu
和ISDISJOIN
这里,与基于
set
的实现不同,复合操作总是使用重写的基本方法,例如
(self.\uu或uuu()
,union)是根据self.add()
实现的,因此它提供了正确的IdentitySet
语义。不,黑客集不会工作,因为set,与字典一样,使用依赖于\uuuuu eq\uuu
和\uuuu hash\uuuu
方法的哈希表。更改\uuuuu eq\uuuu
方法,使其不将这两个点作为相等点进行比较。您可能希望使用list而不是set.:)您不想使用多集吗?@MartijnPieters:是否可以定义一个set的后代--可能是“class IdentitySet(set)”,它会覆盖比较运算符以对其元素进行标识比较?@oao:不,多集会做一些不同的事情。我的应用程序的上下文是,我正在创建、存储和忘记一个对象,而这个对象在图像处理任务的上下文中又有多个子对象。子对象中有多个图像实例,每个实例都有自己的“范围”字段。由于许多图像具有公共范围(例如,640x480),因此有许多具有相同值但不同标识的点实例(每个图像一个)。我需要在我的identitySet中维护每个对象,但同一个对象不能有多个。这似乎有效,我将在下周花一些时间来确认。这是一种我不会想到的方法,我喜欢这种方法。如果你使用set
作为基类,你可能需要覆盖更多的方法,否则如果你调用这些方法,结果可能会不正确,例如。,set.update
可能在没有self.add
@J.F.Sebastian的情况下实现。我发现使用set
作为基类确实存在问题。继承的方法似乎可用于IdentitySet
的其他实例,但无法用于标准set
对象。还有其他问题,;例如set(IdentitySet([1,2,3])
生成set([Ref..Ref..Ref..Ref..Ref..Ref..]
,这可能是由于一种优化,如果参数是集,则直接复制数据。我认为这是无法避免的。我将更改我的代码以继承与您类似的表单MutableSet
。这看起来是正确的方法。正如我在上面对@J.F.Sebastion的评论中所指出的,我今天一直在使用这种方法,而且似乎效果很好。自从最初发布以来,您所做的更改使它有了很大的改进。我现在倾向于这种方法。Python的一个“特性”(使用“特性”是有文档记录的bug的格言)是具有相同值的long(可能还有其他不可变数字)的实例没有相同的标识。因此,我将不得不使用long(恶心)的特例实例,包装器使这一点更加简单。点需要支持值比较,以便它们方便地参与图像处理常见的算法。价值确实不同于身份。我想将@tom的方法与扩展集合和重写其协议进行比较。我看不到一种实用的方法,可以在添加和删除元素时直接钩住集合方法来支持身份比较。我将采用@tom的方法。还有更多。实际上我并不是建议你用它来帮助修改set
,我的意思是你可以用字典代替set。不管怎样,请看
a = (1, 2)
print IdentitySet([a, (1, 2), a])
# -> IdentitySet([(1, 2), (1, 2)]) # only one instance of `a`
print s != Set([a, (1, 2), a]) # it might be unequal because new literal
# tuple (1, 2) might have a different id
print s | Set([a, (1, 2), a])
# -> IdentitySet([(1, 2), (1, 2), (1, 2)]) # `a` plus two tuples from literals