【问题标题】:Is there a Python equivalent of Java's IdentityHashMap?是否有与 Java 的 IdentityHashMap 等效的 Python?
【发布时间】:2013-06-07 21:23:56
【问题描述】:

我正在遍历一个数据结构并想构建一个字典映射 X->Y,其中 X 是我正在遍历的数据结构中的一个字段,Y 是我正在构建的数据结构中的一个字段苍蝇。 X 是不可散列的类型。

【问题讨论】:

  • 如果 X 是不可散列的,那么有理由认为它可以改变(使映射有点困难)。你想让那个“X”实例总是指向那个“Y”实例吗?或者您是否希望该值的任何 X 指向该“Y”。如果它是第一个,那么您可以为每个 X 分配一个 id,然后该 id 应映射到“Y”。如果是第二个,那么您可以将 X 临时存储在可散列容器中,例如元组并将其用作键。我确信可能有更快的方法来做到这一点(我的蟒蛇有点生锈),但它应该可以工作。
  • @Xonar:很确定你不能散列包含不可散列项的元组
  • 是的,是的。 (我确实说过我的 python 生锈了 :))但是您可以递归地将不可散列的项目添加到元组中。例如将 (1,2,[2,3]) 转换为 (1,2,(2,3)) 感谢您指出这一点。

标签: java python object-identity


【解决方案1】:

Java 的 IdentityHashMap 的目的是模拟动态字段。由于 Python 语言已经直接支持动态属性,所以不需要映射,只需将 Y 分配给 X 的属性

x.someSuchRelation = y;

【讨论】:

  • 并非所有 Python 中的对象都支持动态属性!
  • 这需要您为字段选择一个唯一的名称
【解决方案2】:

琐碎:

idmap = {}
idmap[id(x)] = y

使用xid 作为字典键

【讨论】:

  • 请注意,对于自定义类,hash(x) 默认为 id(x) - 您会发现 id 在某些情况下在这里是不必要的。
  • 请注意,x 必须通过其他地方的引用来保证其id 不会被重复使用。
  • 投反对票,因为正如@DavisHerring 所说,它被巧妙地破坏了,可能导致随机错误,因为 id 可以重复使用
【解决方案3】:

如果您将不可散列的对象包装在另一个对象中,则可以为此使用常规 Python dict。具体来说,是这样的:

class Wrapper(object):
    def __init__(self, o):
        self.o = o

    def __hash__(self):
        return id(self.o)

    def __eq__(self, o):
        return hash(self) == hash(o)

然后像some_dict[Wrapper(unhashable_object)]一样使用它。

如果您之后还需要能够访问对象本身(显然是key.o),这是一种比仅使用id(o) 作为键更有用的方法。如果您不这样做(并且垃圾收集不是问题),请使用它。

【讨论】:

  • 您不应该在哈希相等方面实现相等 - py_hash_t 的大小可能小于指针的大小。
【解决方案4】:

通常,对这个常见问题给出的错误解决方案是使用id。 它被破坏是因为id 仅在现有对象中是唯一的,因此可能会随机发生以下情况:

>>> idmap = {}
>>> idmap[id(x)] = 42
>>> del x
>>> z = SomeObject()
>>> z in idmap
True

不需要显式的del 语句,只需在函数内向 idmap 添加一个键就可以得到相同的结果:

>>> def add_smthg(idmap):
>>>     x = SomeObject()
>>>     idmap[id(x)] = 42

>>> idmap = {}
>>> add_smthg(idmap)
>>> z = SomeObject()
>>> z in idmap
True

为避免这种情况,您必须保留插入的每个对象的引用。恕我直言,唯一可行的选择是创建新的字典/集合类:

class IdentitySet:
    def __init__(self, items=None):
        if items is None:
            items = []

        self._identities = {id(item): item for item in items}

    def add(self, item):
        self._identities[id(item)] = item
    
    def __delitem__(self, item):
        del self._identities[id(item)]

    def __contains__(self, item):
        return id(item) in self._identities


class IdentityDict:
    def __init__(self, pairs=None):
        if pairs is None:
            pairs = []

        self._identities = IdentitySet(k for k, _ in pairs)
        self._values = {id(k): v for k, v in pairs}

    def __getitem__(self, item):
        return self._values[id(item)]

    def __setitem__(self, item, value):
        self._identities.add(item)
        self._values[id(item)] = value
    
    def __delitem__(self, item):        
        del self._identities[item]
        del self._values[id(item)]
    
    def __contains__(self, item):
        return item in self._identities

【讨论】:

    猜你喜欢
    • 2010-12-19
    • 1970-01-01
    • 1970-01-01
    • 2011-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-04
    相关资源
    最近更新 更多