Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中的IdentitySet?_Python_Python 2.7 - Fatal编程技术网

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