Immutable Dictionaries for Zope Products
Terry Hancock
hancock at anansispaceworks.com
Sun Dec 1 04:38:45 EST 2002
I got tired of pussyfooting around with ZODB's limits on handling immutable
objects, and I really like dictionaries conceptually, so I decided to define
a read-only, hashable dictionary. For short dictionaries you don't need to
assign to, this should be an adequate replacement, and it "avoids all that
tedious mucking about" with _v_ and __setstate__().
Thought I'd just post the code, since it's short.
I think it supports all the dictionary methods that make sense for read-only
use, and __dict__() to get your original dictionary back. Because it's
hashable, ZODB should accept it as an object attribute, just like a tuple,
without complaint, and it will be persistent in Zope. Just wrap your
dictionary definition like so:
class Geographic(Folder):
meta_type='Geographic'
# ...
Directions = ro_dict({
'N':Compass.N(), 'E':Compass.E(),
'S':Compass.S(), 'W':Compass.W() })
# ....
I hope others find this useful, too. :-)
Cheers,
Terry
Usage example session:
>>> from ZPutils import ro_dict
>>> d = {'a':1, 'b':2, 'c':3}
>>> hash(d)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: dict objects are unhashable
>>> r = ro_dict(d)
>>> r
r{'a': 1, 'b': 2, 'c': 3}
>>> hash(r)
135615604
>>> r['b']
2
>>> r.get('b', default=4)
2
>>> r.get('d', default=4)
4
>>> r.keys()
('a', 'b', 'c')
>>> e = dict(r)
>>> e
{'a': 1, 'c': 3, 'b': 2}
>>> hash(e)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: dict objects are unhashable
>>> r['d'] = 4
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "ZPutils.py", line 78, in __setitem__
raise TypeError("ro_dict does not support assignment")
TypeError: ro_dict does not support assignment
Code:
# (C)2002 Anansi Spaceworks
#------------------------------------------------------------------
# ZPutils
"""
Zope Product-writing utilities:
* ro_dict() -- immutable dictionaries
I imagine I'll find other stuff that needs to be in here over
time. This is meant to be a general collection of useful stuff
for writing Zope Products.
"""
#------------------------------------------------------------------
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#------------------------------------------------------------------
class ro_dict:
"""
Dictionary-like mapping which is immutable and hashable.
Convenient for use in Zope objects where you don't want to
go to the trouble of using a real dictionary and providing
__setstate__ etc, but you do want the convenience of named
mappings.
"""
def __init__(self, d):
"""
Create a read-only dictionary based on the dictionary d.
The idea is that you can write, e.g.,
rd = ro_dict({'my':1, 'special':2, '(0,1)':3, 'Dictionary':4})
to make a read-only dictionary.
"""
keys = d.keys()
keys.sort() # Might as well guarantee order, too.
self.keylist = tuple(keys)
self.valuelist = tuple([d[k] for k in keys])
self.itemlist = tuple(zip(self.keylist,self.valuelist))
self.length = len(keys)
def __repr__(self):
return ('r{'
+
', '.join([ "%s: %s" % (repr(key),repr(val))
for key,val in self.itemlist])
+
'}')
def __getitem__(self, key):
return self.valuelist[ list(self.keylist).index(key) ]
def __setitem__(self, key, value):
raise TypeError("ro_dict does not support assignment")
def get(self, key, default=None):
try:
item = self.__getitem__(key)
except:
item = default
return item
def keys(self):
return self.keylist
def values(self):
return self.valuelist
def items(self):
return self.itemlist
def has_key(self, key):
if key in self.keylist:
return 1
else:
return 0
def copy(self):
return self
def __dict__(self):
"""
Special method to return a real dictionary if we want one.
In Python 2.2, this causes the built-in dict() to work correctly,
in Python 2.1, you have to call it explicitly.
"""
d = {}
for key, val in self.itemlist:
d[key]=val
return d
--
Terry Hancock ( hancock at anansispaceworks.com )
Anansi Spaceworks http://www.anansispaceworks.com
More information about the Python-list
mailing list