A dictionary that has case-insensitive keys.
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | class KeyInsensitiveDict:
"""Dictionary, that has case-insensitive keys.
Keys are retained in their original form
when queried with .keys() or .items().
Implementation: An internal dictionary maps lowercase
keys to (key,value) pairs. All key lookups are done
against the lowercase keys, but all methods that expose
keys to the user retrieve the original keys."""
def __init__(self, dict=None):
"""Create an empty dictionary, or update from 'dict'."""
self._dict = {}
if dict:
self.update(dict)
def __getitem__(self, key):
"""Retrieve the value associated with 'key' (in any case)."""
k = key.lower()
return self._dict[k][1]
def __setitem__(self, key, value):
"""Associate 'value' with 'key'. If 'key' already exists, but
in different case, it will be replaced."""
k = key.lower()
self._dict[k] = (key, value)
def has_key(self, key):
"""Case insensitive test wether 'key' exists."""
k = key.lower()
return self._dict.has_key(k)
def keys(self):
"""List of keys in their original case."""
return [v[0] for v in self._dict.values()]
def values(self):
"""List of values."""
return [v[1] for v in self._dict.values()]
def items(self):
"""List of (key,value) pairs."""
return self._dict.values()
def get(self, key, default=None):
"""Retrieve value associated with 'key' or return default value
if 'key' doesn't exist."""
try:
return self[key]
except KeyError:
return default
def setdefault(self, key, default):
"""If 'key' doesn't exists, associate it with the 'default' value.
Return value associated with 'key'."""
if not self.has_key(key):
self[key] = default
return self[key]
def update(self, dict):
"""Copy (key,value) pairs from 'dict'."""
for k,v in dict.items():
self[k] = v
def __repr__(self):
"""String representation of the dictionary."""
items = ", ".join([("%r: %r" % (k,v)) for k,v in self.items()])
return "{%s}" % items
def __str__(self):
"""String representation of the dictionary."""
return repr(self)
|
This class is useful when associating data with an environment that is case-insensitive, but retains case (such as the Windows filesystem).
The class could be expanded by allowing the user to define a 'translator'-function (defaults to string.lower) that is used to normalize the keys. One concrete example where such feature would be desirable is the IRC protocol, where "[]\~".lower() == "{}|^".
Missing methods. You're missing a couple of dictionary methods:
One more. For testing 'key in dict'
Class Doesn't Respond as a Dictionary. This class doesn't respond to iteration as a normal dictionary. Here is an example of it failing:
===firstly with a normal dictionary
... print key
...
joe
fred
=============and then with the one proposed here
... print key
Traceback (most recent call last):
File "", line 1, in ?
File "CIMCommon.py", line 231, in __getitem__
AttributeError: 'int' object has no attribute 'lower'
This would seem to be an error with the __getitem__ function.
Iterator Improvement. The problem described above of failing with the statement
can be solved by adding simple __iter__() and next() methods. However, there is still an issue:
Reworked Version of Algorithm. In order to satisfy the requirements of supporting
and
I have augmented the code of this algorithm as follows. This seems to satisfy at least all of the tests which I have thrown at it:
(comment continued...)
(...continued from previous comment)
Get method missing. To round off things:
Another approach, with 2.4. This relies on the fact that in 2.4 you can derive from 'dict' like any other class. Also, note that this /changes/ the key, to your canonical (here, lowercase) form; it doesn't leave it in the dictionary as given while continuing to match it case-insensitively as the previous examples do:
The idea is to override just what you must and leave as much as possible to the base class. As noted, it doesn't do keyword initialization. Other than that it should be pretty complete, and hopefully no slower than it must be.
Don't make a case insensitive dictionary. Implement a case insensitive version of a string instead. In that way, you can add case insensitive functionality with any collection.