Commit 8ddc176e authored by Raymond Hettinger's avatar Raymond Hettinger

Improve DictMixin.

Replaced docstring with comments.  Prevents subclass contamination.
Added the missing __cmp__() method and a test for __cmp__().
Used try/except style in preference to has_key() followed by a look-up.
Used iteritem() where possible to save creating a long key list and
   to save redundant lookups.
Expanded .update() to look for the most helpful methods first and gradually
   work down to a mininum expected interface.
Expanded documentation to be more clear on how to use the class.
parent 782d9408
...@@ -43,19 +43,20 @@ class. ...@@ -43,19 +43,20 @@ class.
\begin{classdesc}{DictMixin}{} \begin{classdesc}{DictMixin}{}
Mixin defining all dictionary methods for classes that already have Mixin defining all dictionary methods for classes that already have
a minimum dictionary interface including\method{__getitem__}, a minimum dictionary interface including \method{__getitem__()},
\method{__setitem__}, \method{__delitem__}, and \method{keys}. \method{__setitem__()}, \method{__delitem__()}, and \method{keys()}.
This mixin should be used as a superclass. Adding each of the This mixin should be used as a superclass. Adding each of the
above methods adds progressively more functionality. For instance, above methods adds progressively more functionality. For instance,
the absence of \method{__delitem__} precludes only \method{pop} defining all but \method{__delitem__} will preclude only \method{pop}
and \method{popitem}. and \method{popitem} from the full interface.
While the four methods listed above are sufficient to support the In addition to the four base methods, progessively more efficiency
entire dictionary interface, progessively more efficiency comes comes with defining \method{__contains__()}, \method{__iter__()}, and
with defining \method{__contains__}, \method{__iter__}, and \method{iteritems()}.
\method{iteritems}.
Since the mixin has no knowledge of the subclass constructor, it
does not define \method{__init__()} or \method{copy()}.
\end{classdesc} \end{classdesc}
......
...@@ -62,13 +62,17 @@ class IterableUserDict(UserDict): ...@@ -62,13 +62,17 @@ class IterableUserDict(UserDict):
return iter(self.data) return iter(self.data)
class DictMixin: class DictMixin:
'''Mixin defining all dictionary methods for classes that already have # Mixin defining all dictionary methods for classes that already have
a minimum dictionary interface including getitem, setitem, delitem, # a minimum dictionary interface including getitem, setitem, delitem,
and keys ''' # and keys. Without knowledge of the subclass constructor, the mixin
# does not define __init__() or copy(). In addition to the four base
# methods, progessively more efficiency comes with defining
# __contains__(), __iter__(), and iteritems().
# first level provided by subclass: getitem, setitem, delitem, and keys # second level definitions support higher levels
def __iter__(self):
# second level definitions which assume only getitem and keys for k in self.keys():
yield k
def has_key(self, key): def has_key(self, key):
try: try:
value = self[key] value = self[key]
...@@ -76,34 +80,30 @@ class DictMixin: ...@@ -76,34 +80,30 @@ class DictMixin:
return False return False
return True return True
__contains__ = has_key __contains__ = has_key
def __iter__(self):
for k in self.keys():
yield k
def __len__(self):
return len(self.keys())
# third level uses second level instead of first # third level takes advantage of second level definitions
def iteritems(self): def iteritems(self):
for k in self: for k in self:
yield (k, self[k]) yield (k, self[k])
iterkeys = __iter__ iterkeys = __iter__
# fourth level uses second and third levels instead of first # fourth level uses definitions from lower levels
def itervalues(self): def itervalues(self):
for _, v in self.iteritems(): for _, v in self.iteritems():
yield v yield v
def values(self): def values(self):
return [self[key] for key in self.keys()] return [v for _, v in self.iteritems()]
def items(self): def items(self):
return list(self.iteritems()) return list(self.iteritems())
def clear(self): def clear(self):
for key in self.keys(): for key in self.keys():
del self[key] del self[key]
def setdefault(self, key, default): def setdefault(self, key, default):
if key not in self: try:
return self[key]
except KeyError:
self[key] = default self[key] = default
return default return default
return self[key]
def pop(self, key): def pop(self, key):
value = self[key] value = self[key]
del self[key] del self[key]
...@@ -112,15 +112,30 @@ class DictMixin: ...@@ -112,15 +112,30 @@ class DictMixin:
try: try:
k, v = self.iteritems().next() k, v = self.iteritems().next()
except StopIteration: except StopIteration:
raise KeyError, 'dictionary is empty' raise KeyError, 'container is empty'
del self[k] del self[k]
return (k, v) return (k, v)
def update(self, other): def update(self, other):
for key in other.keys(): # Make progressively weaker assumptions about "other"
self[key] = other[key] if hasattr(other, 'iteritems'): # iteritems saves memory and lookups
for k, v in other.iteritems():
self[k] = v
elif hasattr(other, '__iter__'): # iter saves memory
for k in other:
self[k] = other[k]
else:
for k in other.keys():
self[k] = other[k]
def get(self, key, default=None): def get(self, key, default=None):
if key in self: try:
return self[key] return self[key]
return default except KeyError:
return default
def __repr__(self): def __repr__(self):
return repr(dict(self.items())) return repr(dict(self.iteritems()))
def __cmp__(self, other):
if isinstance(other, DictMixin):
other = dict(other.iteritems())
return cmp(dict(self.iteritems()), other)
def __len__(self):
return len(self.keys())
...@@ -210,9 +210,8 @@ else: ...@@ -210,9 +210,8 @@ else:
s.update({10: 'ten', 20:'twenty'}) # update s.update({10: 'ten', 20:'twenty'}) # update
verify(s[10]=='ten' and s[20]=='twenty') verify(s[10]=='ten' and s[20]=='twenty')
verify(s == {10: 'ten', 20:'twenty'}) # cmp
t = SeqDict()
t[20] = 'twenty'
t[10] = 'ten'
verify(s == t)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment