On Mon, 10 Dec 2012 22:48:50 -0500, Dave Cinege wrote:

> Thesaurus: A different way to call a dictionary.

Is this intended as a ready-for-production class?


> Thesaurus is a new a dictionary subclass which allows calling keys as if
> they are class attributes and will search through nested objects
> recursively when __getitem__ is called.


If only that were true...

py> d = Thesaurus()
py> d['spam'] = {}
py> d['spam']['ham'] = 'cheese'  # key access works
py> d.spam.ham  # but attribute access doesn't
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'ham'



> You will notice that the code is disgusting simple. However I have found
> that this has completely changed the way I program in python.

By making it less robust and more prone to errors?


py> d = Thesaurus()
py> d.copy = "some value"
py> assert d.copy == "some value"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError


Operations which silently fail or do the wrong thing are not a good 
thing. Scarily, this isn't even consistent:

py> "%(copy)s" % d
'some access'
py> d.copy
<built-in method copy of Thesaurus object at 0xb717a65c>



Some further comments about your code:


> class Thesaurus (dict):
>       [...]
>       def __getitem__(self, key):
>               if '.' not in key:
>                       return dict.__getitem__(self, key)
>               l = key.split('.')
>               if isinstance(l[0], (dict, Thesaurus)):
>                       a = self.data

Testing for isinstance (dict, Thesaurus) is redundant, since Thesaurus is 
a subclass of dict.

More importantly, there are no circumstances where l[0] will be a dict. 
Since l is created by splitting a string, l[0] must be a string and this 
clause is dead code.

Which is good, because self.data is not defined anywhere, so if you ever 
did reach this branch, your code would fail.


>               else:
>                       a = self
>               for i in range(len(l)):         # Walk keys recursivly


This is usually better written as:

for subkey in l:
    # look up subkey
    # or if all else fails
    raise KeyError(subkey)


KeyError('spam.ham.cheese [1]') is misleading, since it implies to me 
that looking up spam.ham.cheese succeeded, and the failed lookup was on 
1. I would expect either of these:

KeyError('spam.ham')
KeyErroor('ham')

with a slight preference for the first.

Further issues with your code:

>                       try:
>                               a = a[l[i]]             # Attempt key
>                       except:


Bare excepts like this are not good. At best they are lazy and sloppy, 
good only for throw-away code. At worst, they are hide bugs. In this 
case, they can hide bugs:


py> class X(object):
...     @property
...     def test(self):
...             return 1/0  # oops a bug
... 
py> d = Thesaurus(a=X())
py> d.a.test  # Gives the correct exception for the error.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in test
ZeroDivisionError: float division
py>
py> "%(a.test)s" % d  # Lies about the problem.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 29, in __getitem__
KeyError: 'a.test [1]'



One more comment:


> # I like to create a master global object called 'g'.
> # No more 'global' statements for me.
> g = thes()

This is not a good thing. Encouraging the use of globals is a bad thing.



-- 
Steven
-- 
http://mail.python.org/mailman/listinfo/python-list

Reply via email to