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 17 years, 7 months 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

Aristotelis Mikropoulos (author) 17 years, 7 months ago  # | flag

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

Aristotelis Mikropoulos (author) 17 years, 7 months ago  # | flag

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

vincent delft 17 years, 7 months 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 17 years, 7 months 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 17 years, 7 months 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 17 years, 7 months ago  # | flag

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

tomer filiba 17 years, 7 months ago  # | flag

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

thanos vassilakis 16 years, 10 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 12 years, 5 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