Welcome, guest | Sign In | My Account | Store | Cart

The KeyedDict object keeps a list of approved key values. If you try to assigned an unapproved key to a value, an exception is thrown.

When you create the object, you give it a sequence (tuple, list, etc.) of key objects and then, when you set an item with a key, it will make sure that the key is in the "approved" list. If you try to set an item with a key that isn't in your approved list, you get an TypeError exception.

Python, 28 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from sets import Set
import string

class KeyedDict(dict):

    def __init__(self,keys):
        self.__keys = Set(keys)

    def __setitem__(self,key,val):
        if key not in self.__keys:
            keylist = string.join(self.__keys,",")
            raise TypeError("Tried to use key '"+key+"'.\nCan only add items with one of the following keys: "+keylist)

        return dict.__setitem__(self,key,val)

if __name__ == '__main__':

    d = KeyedDict(("fred","barney"))

    ## these should work
    d['fred']='flintstone'
    d['barney']='rubble'

    ## now catch the exception
    try:
        d['wilma']='flintstone'
    except TypeError,e:
        print "TypeError correctly thrown"

I was using the DictWriter object in the csv module and got caught when the keys in my dictionary did not match the key list I used when I created the DictWriter object. I whipped up this class to help me solve the issue and, though simple, it was very useful for that purpose and perhaps many more.

Using a Set to keep the list of keys lets you use a large number of keys with minimal (yet existent) performance impact.

Instead of throwing the error, the KeyedDict could just do nothing and keep on going. That said, you could just do an "except: pass" as well.

2 comments

Michael Watkins 17 years, 3 months ago  # | flag

And to force consistency in types in keys or values... There's a general purpose module in the 'QP' package (see qp.lib.spec) which provides a useful "specification" capability which you might also find interesting:

->> from qp.lib.spec import mapping, require

# require that all keys of the dict be of type 'str' and values be of type 'int'
->> myspec = mapping({str:int}, dict)
->> require({'foo':123,'bar':456}, myspec)
->> require({'foo':123,'bar':'456'}, myspec)
Traceback (most recent call last):
...
TypeError:
  Expected: mapping({str: int}, anything)
  Got: {'foo': 123, 'bar': '456'}


# require that all keys of the dict be of type 'str' and values be of type 'int', and that key values be of specific values
->> myspec2 = mapping({('foo','bar'):int}, dict)
->> require({'foo':123,'bar':456}, myspec)
->> require({'foo':123,'bar':456}, myspec2)
->> require({'foo':123,'bars':456}, myspec2)
Traceback (most recent call last):
...
TypeError:
  Expected: mapping({('foo', 'bar'): int}, dict)
  Got: {'bars': 456, 'foo': 123}

Its worth having a read through the "spec" module; its small enough to digest easily and can be used quite independently of the qp web framework. To see it in practical use, and discover how it can be used to replace tons of boilerplate initialization and value checking code, see also the "Dulcinea" package, also at the link below. Its a library of objects and user interfaces; the object definitions are generally very easy to understand and made simpler because of "spec". After discovering spec I went through a code refactoring exercise and was very pleased with the outcome.

http://www.mems-exchange.org/software/

Christopher Dunn 17 years, 2 months ago  # | flag

Why not raise KeyError instead of TypeError?

Created by Mike Hostetler on Mon, 4 Dec 2006 (PSF)
Python recipes (4591)
Mike Hostetler's recipes (3)

Required Modules

Other Information and Tasks