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