ANNOUNCE: Thesaurus - a recursive dictionary subclass using attributes

D

Dave Cinege

Thesaurus: A different way to call a dictionary.

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.

You will notice that the code is disgusting simple. However I have found that
this has completely changed the way I program in python. I've re-written some
exiting programs using Thesaurus, and often realized 15-30% code reduction.
Additionally I find the new code much easier to read.

If you find yourself programing with nested dictionaries often, fighting to
generate output or command lines for external programs, or wish you had
a dictionary that could act (sort of) like a class, Thesaurus may be for you.
 
S

Steven D'Aprano

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] # 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.
 
D

Dave Cinege

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

For me, yes. In production code.
py> d = Thesaurus()
py> d['spam'] = {}

Maybe because spam is type dict instead of type thes???
import thesaurus
thes = thesaurus.Thesaurus
t = thes()
t.spam = thes()
t.spam.ham = 'cheese'
print t.spam.ham cheese
print t['spam'].ham cheese
print t['spam']['ham'] cheese
'%(spam.ham)s' % t
'cheese'

Works for me!

Remainder of your post, not productive, not worth my time.

Dave
 
S

Steven D'Aprano

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

For me, yes. In production code.
py> d = Thesaurus()
py> d['spam'] = {}

Maybe because spam is type dict instead of type thes???

Well duh. By the way, it's a dirty trick to cut out all context to try to
make me seem like an idiot.

In context, you stated that Thesaurus "will search through nested objects
recursively when __getitem__ is called", but in fact that is not true. It
does not do what you state it does.

Remainder of your post, not productive, not worth my time.

Oh well I'm just hurt now. *sobs*

So, let's see now... I identified that your Thesaurus code:

* will fail silently;
* contains dead code that is never used;
* contains redundant code that is pointless;
* hides errors in the caller's code;

and you consider this "not productive, not worth my time". Code review
with you must be *all* sorts of fun.
 
S

Steven D'Aprano

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

For me, yes. In production code.
py> d = Thesaurus()
py> d['spam'] = {}

Maybe because spam is type dict instead of type thes???

Well duh. By the way, it's a dirty trick to cut out all context to try to
make me seem like an idiot.

In context, you stated that Thesaurus "will search through nested objects
recursively when __getitem__ is called", but in fact that is not true. It
does not do what you state it does.

Remainder of your post, not productive, not worth my time.

Oh well I'm just hurt now. *sobs*

So, let's see now... I identified that your Thesaurus code:

* will fail silently;
* contains dead code that is never used;
* contains redundant code that is pointless;
* hides errors in the caller's code;

and you consider this "not productive, not worth my time". Code review
with you must be *all* sorts of fun.
 
C

Chris Angelico

So, let's see now... I identified that your Thesaurus code:

* will fail silently;
* contains dead code that is never used;
* contains redundant code that is pointless;
* hides errors in the caller's code;

and you consider this "not productive, not worth my time". Code review
with you must be *all* sorts of fun.

He never asked for code review :)

ChrisA
 
D

Dave Cinege

On Wednesday 12 December 2012 05:25:11 D'Arcy J.M. Cain wrote:

As a 16yr OSS vet I know that for every 1 person that that actually creates
something there will always be 2000 people to bitch about it. My skin isn't
thin, I just don't give a shit to listen to anyone one that doesn't get it.

The point to Thesaurus for those that want to pay attention:
The concept in these ~25 lines of code have changed the way I program Python
and reduced existing functionally identical code up to 30%...and I like the
code better.

If that doesn't peak your interest, then move on...nothing here for you to
see.

If you feel it needs to be expanded/corrected, do it and share it. If you can
do it better, re-implement it. That's why I sent it to the mailing list.
 
M

Mark Lawrence

On Wednesday 12 December 2012 05:25:11 D'Arcy J.M. Cain wrote:

As a 16yr OSS vet I know that for every 1 person that that actually creates
something there will always be 2000 people to bitch about it. My skin isn't
thin, I just don't give a shit to listen to anyone one that doesn't get it.

The point to Thesaurus for those that want to pay attention:
The concept in these ~25 lines of code have changed the way I program Python
and reduced existing functionally identical code up to 30%...and I like the
code better.

If that doesn't peak your interest, then move on...nothing here for you to
see.

Please don't place responses like this. The Python community prides
itself on tolerance. If you don't wish to follow that recommendation
please go to an alternative site.
 
S

Steven D'Aprano

Please don't place responses like this. The Python community prides
itself on tolerance. If you don't wish to follow that recommendation
please go to an alternative site.

Well, I must say, I think that you've just won an award for Most
Cognitive Dissonance Exhibited In The Shortest Time. I'm not sure how you
can yell at somebody that Google users should be dead in one post, and
then nine minutes later say this.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top