obj[1] *and* obj['foo']

Bengt Richter bokr at oz.net
Wed Aug 7 04:43:06 CEST 2002


On Tue, 06 Aug 2002 22:11:29 -0000, Travis Shirk <travis at pobox.com> wrote:

>I'm trying to write a class that implements __getitem__(self, k),
>but I'd like to invoke the method with integer keys and string keys.
>Currently, I'm getting exceptions when I use string keys:
>
>TypeError: sequence index must be integer
>
>I'm subclassing list (python 2.2) so I understand why the *sequence* does
>not allow non-int keys, and I assume that if I subclassed dict the reverse
>type clash would occur.
>
No, dicts like anything hashable, and that includes numbers.
E.g., suppose you wanted a weird dictionary with a restricted list of legal numeric keys:
(so we have a reason for not using a plain old dict in the example ;-)
 >>> class D(dict):
 ...     def __init__(self, legalNumKeys): self.legalNumKeys = legalNumKeys
 ...     def __getitem__(self, k):
 ...         if isinstance(k, (int,float)) and k not in self.legalNumKeys:
 ...             raise KeyError, 'Numeric key not in legal list'
 ...         return dict.__getitem__(self, k)
 ...     def __setitem__(self, k, v):
 ...         if isinstance(k, (int,float)) and k not in self.legalNumKeys:
 ...             raise KeyError, 'Numeric key not in legal list'
 ...         dict.__setitem__(self, k, v)
 ...
 >>> d=D((2, 3, 7, 4.5))
 >>> d[2]='two'
 >>> d['two']=2
 >>> d[4.5]='four-point-five'
 >>> d[4.6]='a little more'
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 9, in __setitem__
 KeyError: Numeric key not in legal list
 >>> d[4]='four?'
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 9, in __setitem__
 KeyError: Numeric key not in legal list
 >>> d[2]
 'two'
 >>> d['two']
 2
 >>> d[4.5]
 'four-point-five'


>So, is there a way to "override" __getitem__ to accept both types of
>access?
An ordinary dict will do that. The thing is whether you need ordering
for some reason, and why you need it. Otherwise, the larger your set
of keys (unless they are just integer indices of a list), the more
the advantage goes to the dict.

BTW, notice something about numeric dictionary keys:
 >>> d[1.5*3]
 'four-point-five'
 >>> d[(.06)*100-1.5]
 'four-point-five'
 >>> d[(.17-.11)*100-1.5]
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 5, in __getitem__
 KeyError: Numeric key not in legal list

I.e., watch out for computed values of floating point keys trying to match
constants. Floating point can be exact but it can also be inexact, so you
have to know what you are doing. Constants should match constants, though.

Also note that numerical equality makes for equal keys:

 >>> d[2.0]
 'two'
 >>> d[2]
 'two'

But a string is not a number, even if it looks equal in numeric value:
 >>> d['2']
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 6, in __getitem__
 KeyError: 2
           ^-- hm, that's a little misleading. IMO it should print '2'

So what is it you really want to do? Do you have a solution and you're
looking for help in shoehorning your real problem into that, or do you have
a problem and the problem is really the thing you want help with? ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list