简体   繁体   中英

Is there a Python equivalent of Java's IdentityHashMap?

I'm walking a data structure and would like to build a dict mapping X->Y, where X is a field in the data structure I'm walking and Y is a field in the data structure I'm building on the fly. X is an unhashable type.

The purpose of Java's IdentityHashMap is to simulate dynamic field. Since Python language already supports dynamic attributes directly, you don't need the map, just assign Y to an X's attribute

x.someSuchRelation = y;

You can just use a regular Python dict for this if you wrap your unhashable objects in another object. Specifically, something like this:

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)

Then just use it like some_dict[Wrapper(unhashable_object)] .

This is a more useful approach than just using id(o) as the key if you also need to be able to access the object itself afterwards (as key.o , obviously). If you don't (and garbage collection isn't an issue), just use that.

Trivially:

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

Use the id of x as the dictionary key

Often, the broken solution given to this common problem is to use id . It is broken because id is only unique among existing objects, so the following can randomly happen:

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

No explicit del statement has to happen, just adding a key to idmap inside a function could lead to the same result:

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

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

To avoid this, you have to keep a reference of each object you insert. IMHO the only viable option is to create new dictionnary / set classes:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM