Insensitive Dictionaries (Was: Language change and code breaks)

Joal Heagney s713221 at student.gu.edu.au
Sun Jul 22 23:59:18 EDT 2001


Peter Mayne wrote:

> Or should dictionaries be changed so that key lookups
> become case-insensitive? That would mean a program that looks for different
> cases of variables and produces example output of {'STRING': 3, 'String': 2,
> 'string': 3} (posted elsewhere in this thread) couldn't be written. 8-)
> Maybe we should change only some dictionaries. I know I'm not going to
> figure out the "right" way of fixing this.
> 
> We could (for instance) assume that all variable names are stored in lower
> case, but then A.__dict__['F1']() wouldn't work. How do you explain that
> defining a name in upper case, then doing introspection using the same case,
> doesn't work, just after you've explained that the language is
> case-insensitive? For that matter, how do you explain to beginners that
> Python might be case-insensitive, but 'Python'!='python'?

Well if all we want is some dictionaries to be insensitive (The
cold-hearted bastards *grins*), would this do to begin with? Of course
it needs some expansion so that tuples are expanded insensitively as
well. (Left as an exercise for the reader.*grins*)

>>> from UserDict import UserDict
>>> class InsensitiveDict(UserDict):
	def __getitem__(self,key):
		if type(key) == type(""):
			return self.data[key.lower()]
		else:
			return self.__dict[key]
	def __setitem__(self,key,value):
		if type(key) == type(""):
			self.data[key.lower()] = value
		else:
			self.data[key] = value

			
>>> a = InsensitiveDict()
>>> a['a'] = 1
>>> a['A']
1
>>> a['a']
1
>>> a['A'] = 2
>>> a['a']
2

Only hassle is that it isn't internally case-preserving, especially with
item, key and __repr__ methods.
>>> a = InsensitiveDict()
>>> a['A'] = 1
>>> a.keys()
['a']
>>> a.items()
[('a', 1)]
>>> a
{'a': 1}

Number two is an alternative that internally keeps the first key that is
entered, no matter if it's value is redefined by a different cased key.

>>> class InsensitiveDict2(UserDict):
	def __getitem__(self,key):
		if self.data.has_key(key):
			return self.data[key]
		else:
			if type(key) == type(""):
				for i in self.data.keys():
					if i.lower() == key.lower():
						return self.data[i]
				raise KeyError, key
	def __setitem__(self,key,value):
		if self.data.has_key(key):
			self.data[key] = value
		else:
			if type(key) == type(""):
				nothaskey = 1
				for i in self.data.keys():
					if i.lower() == key.lower():
						self.data[i] = value
						haskey = 0
				if nothaskey:
					self.data[key] = value
			else:
				self.data[key] = value

					
>>> b = InsensitiveDict2()
>>> b['A'] = 2
>>> b
{'A': 2}
>>> b['a'] = 3
>>> b
{'A': 3}

Version three keeps the latest case-version key that has been assigned
to in the dictionary. It's the one I prefer most.

>>> class InsensitiveDict3(UserDict):
	def __getitem__(self,key):
		try:
			return self.data[key]
		except IndexError:
			if type(key) == type(""):
				for i in self.data.keys():
					if i.lower() == key.lower():
						return self.data[i]
		else:
			raise IndexError
	def __setitem__(self,key,value):
		if self.data.has_key(key):
			self.data[key] = value
		else:
			if type(key) == type(""):
				for i in self.data.keys():
					if i.lower() == key.lower():
						del self.data[i]
				self.data[key] = value
			else:
				self.data[key] =value

				
>>> b = InsensitiveDict2()
>>> b['A'] = 2
>>> b
{'A': 2}
>>> b['a'] = 3
>>> b
{'a': 3}

All of these need extra logic that can expand tuples to be truely case
insensitive. 

Also, I'm not so sure I like the internal loop over all items in
self.data.keys for the second and third version. Imagine the performance
hits for large dictionaries. Unless there was a need for
case-preservation, in terms of preferences, version one would win out in
this case. Does anyone else have a cleaner method? Perhaps two internal
dictionaries, one case-preserving and one case-insensitive?
-- 
      Joal Heagney is: _____           _____
   /\ _     __   __ _    |     | _  ___  |
  /__\|\  ||   ||__ |\  || |___|/_\|___] |
 /    \ \_||__ ||___| \_|! |   |   \   \ !



More information about the Python-list mailing list