Welcome, guest | Sign In | My Account | Store | Cart

The implementation of a dictionary, whose items cannot be reset or deleted, nor new can be added.

Python, 82 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#!/usr/bin/env python

"""A tuctionary, or tuct, is the combination of a tuple with
a dictionary. A tuct has named items, but they cannot be
deleted or rebound, nor new can be added.
"""

class tuct(object):
	"""The tuct class. An immutable dictionary.
	"""
	
	def __init__(self, dict=None, **kwds):
		self.__data = {}
		if dict is not None:
			self.__data.update(dict)
		if len(kwds):
			self.__data.update(kwds)
	
	del __init__
	
	def __repr__(self):
		return repr(self.__data)
	
	def __cmp__(self, dict):
		if isinstance(dict, tuct):
			return cmp(self.__data, dict.__data)
		else:
			return cmp(self.__data, dict)
	
	def __len__(self):
		return len(self.__data)
	
	def __getitem__(self, key):
		return self.__data[key]
	
	def copy(self):
		if self.__class__ is tuct:
			return tuct(self.__data.copy())
		import copy
		__data = self.__data
		try:
			self.__data = {}
			c = copy.copy(self)
		finally:
			self.__data = __data
		c.update(self)
		return c
	def keys(self):
		return self.__data.keys()
	
	def items(self):
		return self.__data.items()
		
	def iteritems(self):
		return self.__data.iteritems()
		
	def iterkeys(self):
		return self.__data.iterkeys()
	
	def itervalues(self):
		return self.__data.itervalues()
	
	def values(self):
		return self.__data.values()
		
	def has_key(self, key):
		return self.__data.has_key(key)
	
	def get(self, key, failobj=None):
		if not self.has_key(key):
			return failobj
		return self[key]
		
	def __contains__(self, key):
		return key in self.__data
	
	@classmethod
	def fromkeys(cls, iterable, value=None):
		d = cls()
		for key in iterable:
			d[key] = value
		return d

We define our class, which does not have methods that can alter the object.

10 comments

Nilton Volpato 16 years ago  # | flag

Almost worked :). One can still use the __init__ method, which works just like an update.

>>> t = tuct(a=20,c=40,b=30)
>>> t
{'a': 20, 'c': 40, 'b': 30}
>>> t.__init__(a=0,z=1000)
>>> t
{'a': 0, 'c': 40, 'b': 30, 'z': 1000}
>>>

-- Nilton

Speed. Because, if we had derived from dict, the class would be slower.

Recommendation. So, do you recommend me something, in order to prevent this?

vincent delft 16 years ago  # | flag

Why not inherit from dict.

class tuct(dict)
   def __setitem__(self, key, val):
       pass

If I'm not wrong this does the same. But as Nilton mentioned, the via the __init__ you still can update it ;-).

PS: I'm on Python 2.4

faulkner612 16 years ago  # | flag

__hash__. isn't hashability the primary reason for immutability?

class tuct(dict):
    def __setitem__(self, key, value): pass
    def __hash__(self):
        items = self.items()
        res = hash(items[0])
        for item in items[1:]:
            res ^= hash(item)
        return res
Roel Mathys 16 years ago  # | flag

__init__. something like this?

def __init__( self , dict = None , **kwds ) :
    if hasattr(self,'data'):
        raise AttributeError, '__init__'
    self.data = {}
    if dict is not None:
        self.data.update( dict )
        if len( kwds ) :
            self.data.update( kwds )

    del self.__init__

bye, rm

tomer filiba 16 years ago  # | flag

why? slower? it ought to be faster. you should really test that.

tomer filiba 16 years ago  # | flag

why? slower? it ought to be faster. you should really test that.

thanos vassilakis 15 years, 3 months ago  # | flag

RE: __hash__.

>>> class tuct(dict):
...    def __setitem__(self, key, value): pass
...     def __hash__(self):
...         items = self.items()
...         res = hash(items[0])
...         for item in items[1:]:
...             res ^= hash(item)
...         return res

>>> d = tuct(test='test')
>>> d
{'test': 'test'}
>>> d['test'] = 1
>>> d
>>> d.update(test=90)
>>> d
{'test': 90}
>>> d.setdefault('moo', 1)
>>> d
{'test': 90, 'moo': 1}

I think the envelope/letter idiom works well, especially when you need a immutable map for a class attribute or a default value.

Jonathan Hartley 10 years, 11 months ago  # | flag

I'm in two minds about whether it might be useful to define the mutating methods on a class like this, but have them always construct a new tuct and return that (thus, for example, 'a.update(b)' would return a new tuct, 'c', the result of the update operation, while leaving 'a' unmodified.

Created by Aristotelis Mikropoulos on Wed, 6 Sep 2006 (PSF)
Python recipes (4591)
Aristotelis Mikropoulos's recipes (2)

Required Modules

Other Information and Tasks