Python data structure for a collection of objects with random access based on an attribute

后端 未结 4 1758
臣服心动
臣服心动 2021-02-06 15:31

I need a collection of objects which can be looked up by a certain (unique) attribute common to each of the objects. Right now I am using a dicitionary assigning the dictionary

4条回答
  •  借酒劲吻你
    2021-02-06 15:46

    There is actually no duplication of information as you fear: the dict's key, and the object's .key attribute, are just two references to exactly the same object.

    The only real problem is "what if the .key gets reassigned". Well then, clearly you must use a property that updates all the relevant dicts as well as the instance's attribute; so each object must know all the dicts in which it may be enregistered. Ideally one would want to use weak references for the purpose, to avoid circular dependencies, but, alas, you can't take a weakref.ref (or proxy) to a dict. So, I'm using normal references here, instead (the alternative is not to use dict instances but e.g. some special subclass -- not handy).

    def enregister(d, obj):
      obj.ds.append(d)
      d[obj.key] = obj
    
    class Item(object):
        def __init__(self, uniq_key, title=None):
            self._key = uniq_key
            self.title = title
            self.ds = []
    
        def adjust_key(self, newkey):
            newds = [d for d in self.ds if self._key in d]
            for d in newds:
              del d[self._key]
              d[newkey] = self
            self.ds = newds
            self._key = newkey
    
        def get_key(self):
            return self._key
    
        key = property(get_key, adjust_key)
    

    Edit: if you want a single collection with ALL the instances of Item, that's even easier, as you can make the collection a class-level attribute; indeed it can be a WeakValueDictionary to avoid erroneously keeping items alive, if that's what you need. I.e.:

    class Item(object):
    
        all = weakref.WeakValueDictionary()
    
        def __init__(self, uniq_key, title=None):
            self._key = uniq_key
            self.title = title
            # here, if needed, you could check that the key
            # is not ALREADY present in self.all
            self.all[self._key] = self
    
        def adjust_key(self, newkey):
            # "key non-uniqueness" could be checked here too
            del self.all[self._key]
            self.all[newkey] = self
            self._key = newkey
    
        def get_key(self):
            return self._key
    
        key = property(get_key, adjust_key)
    

    Now you can use Item.all['akey'], Item.all.get('akey'), for akey in Item.all:, and so forth -- all the rich functionality of dicts.

提交回复
热议问题