dictionary and __getattr__

Alex Martelli aleax at aleax.it
Thu Sep 6 11:49:25 EDT 2001


"Skip Montanaro" <skip at pobox.com> wrote in message
news:mailman.999787027.17046.python-list at python.org...
    ...
>     Chris> Hey, won't the new 2.2 support for extending built-in classes
>     Chris> such as dictionaries make this both easy and complete?
>
> Yup:
>
>     % ./python
>     Python 2.2a2+ (#23, Sep  5 2001, 21:48:23)
>     [GCC 2.96 20000731 (Linux-Mandrake 8.0 2.96-0.48mdk)] on linux2
>     Type "help", "copyright", "credits" or "license" for more information.
>     >>> dictionary
>     <type 'dictionary'>
>     >>> class AttributeDict(dictionary):
>     ...   __getattr__ = dictionary.__getitem__
>     ...
>     >>> d = AttributeDict()
>     >>> d['a'] = 1
>     >>> d.a
>     1

...*however*...:

C:\Python22>python
Python 2.2a2 (#22, Aug 22 2001, 01:24:03) [MSC 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class AD(dictionary): __getattr__ = dictionary.__getitem__
...
>>> d=AD()
>>> d['a']=1
>>> d.a
1
>>> d.get('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
KeyError: get

In 2.2, except for classic-classes (and a class extending a
builtin type ain't classic:-), __getattr__ is called for EVERY
attribute access.  So, d.get goes through __getitem__, too,
and fails (note the KeyError rather than AttributeError, which
is also common to the UserDictionary version of this!) -- i.e.,
there's no d['get'], so there's no d.get either.  Eeeep...

You really need a bit more work to make a "good" AttributeDictionary
class, anyway:

>>> class AD(dictionary):
...   def __getattr__(self, name):
...     try: return dictionary.__getattr__(self, name)
...     except AttributeError: pass
...     try: return self[name]
...     except KeyError: raise AttributeError, name
...
>>> d=AD()
>>> d['a']=1
>>> d.a
1
>>> d.get('a')
1
>>> d.b
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 6, in __getattr__
AttributeError: b
>>>

Now, we get both the right behavior for d.get(...) AND
the right exception for accessing d.inexistent.

Of course, there ARE problems still -- now, if you set
d['get']=1, you aint' gonna access it as d.get (that's
a bound-method of course) -- and if you switch the
try blocks in AD.__getattr__, then when d['get']=1 is
done, you lose the ability to call d.get('foo') in
the normal way.  Can't have your cake AND eat it too!

But that just underscores how confusing attributes
with items is an intrinsically bad idea (and one of
my pet peeves against Javascript:-)...:-).


Alex






More information about the Python-list mailing list