[Tutor] Prevent "Coder's Remorse"?

Kent Johnson kent37 at tds.net
Wed Sep 28 14:50:43 CEST 2005


Ron Phillips wrote:
> At any rate, I am currently working on a script to model an ordered
> collection of geographic points, all of which must share the same
> collection of attributes. So, a little collection might be:
>  
> pointList = [
> {lat:40.123,lon:-81.456, 
>     'attributes':{'msg':'example','beavers':34, 'distance':18.132}
> },
> {lat:40.12345,lon:-81.45678, 
>     'attributes':{'msg':'','beavers':0, 'distance':0.0}
> }
> ]
>  
> If I add an attribute of 'newAtt':'newVal' to
> pointList[1]['attributes'], I want it to automatically add
> 'newAtt':'default' to
> all the other member's 'attributes' dictionary. If I delete an
> attribute, it should delete from all the member's dictionaries. The
> attributes are limited to string, integer, and float values.
>  
> I can do this by brute force, but:
> 
> Is there an elegant approach that occurs to anyone? 
> Is there a data structure that forces all members to have the same
> keys? 
> Is there an object structure that will let updates to one instance
> affect all instances?
> Am I even asking the right questions?

I'm curious about why you want to do this, it is kind of a strange requirement. But anyway, here is a class that subclasses dict and presents the interface you want to client code. It doesn't actually update all instances when you add or delete a key; it keeps a list of valid keys in a class attribute and updates this list when you add or delete. Then when you access a key it checks the list of valid keys. If the key you want is not in the list, you get a KeyError - the instance dict is never checked. If the key you want is in the list but not in the instance, you get the default value.

I don't see any need to actually add default values to all the dicts. Conceivably to save memory you might want to actually delete values from all the dicts; to do this you could keep a list of all the existing dicts as another class attribute. I'm not sure you will save much though as the dict itself probably doesn't resize downward. A side effect of not deleting keys is that if you delete and then restore a key, instances that had that key before will magically restore their old value. This could be a bug or a feature depending on your requirements.

Finally, the set of valid keys is shared by all instances, so if you want to use this for two different types of things you will have to make subclasses and give each subclass its own _key attribute.

Requires Python 2.3 or greater

Kent

# CommonKeysDict.py
# Make it work with Python 2.3
try:
    set
except NameError:
    from sets import Set as set
    
class CommonKeysDict(dict):
    """Dictionary where all instances must have the same set of keys
       and with a default value for unknown keys.
       
       >>> d = CommonKeysDict()
       >>> d[0]
       Traceback (most recent call last):
         ...
       KeyError: 0
       
       >>> d[0] = 1
       >>> d[0]
       1
       
       A second instance will get the default value for keys that are in use:
       >>> d2 = CommonKeysDict()
       >>> d2[0]
       'default'
       
       The second instance can have its own values
       >>> d2[0] = 2
       >>> d2[0]
       2
       >>> d[0]
       1
       
       Deleting a key from any instance removes that key from the allowed set.
       Note that this doesn't actually delete the key from all instances, it just
       makes it inaccessible.
       
       >>> del d[0]
       >>> d2[0]
       Traceback (most recent call last):
         ...
       KeyError: 0
       
    """
    
    _keys = set()
    
    def __init__(self, **items):
        dict.__init__(self, **items)

    def __getitem__(self, key):
        if key not in self._keys:
            raise KeyError, key
        try: 
            return dict.__getitem__(self, key)
        except KeyError:
            return 'default'


    def __setitem__(self, key, value):
        self._keys.add(key)
        dict.__setitem__(self, key, value)
    
    
    def __delitem__(self, key):
        self._keys.remove(key)
        dict.__delitem__(self, key)
        
        
def _test():
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    _test()




More information about the Tutor mailing list