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

Python internal has type 'dictproxy'.

You can get the dictproxy type object in Python.

from types import DictProxyType

any new-style classes has a __dict__ attribute, it's the dictproxy object. but, the type constructor disallow you to make the instance.

DictProxyType({}) # TypeError

This recipe explains how to make dictproxy object via Python API ... and type() more easy way

Python, 61 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
__all__ = ['dictproxy', 'make_dictproxy']

from ctypes import pythonapi, py_object
from _ctypes import PyObj_FromPtr

PyDictProxy_New = pythonapi.PyDictProxy_New
PyDictProxy_New.argtypes = (py_object,)
PyDictProxy_New.rettype = py_object


def make_dictproxy(obj):
    assert isinstance(obj,dict)
    return PyObj_FromPtr(PyDictProxy_New(obj))
    
## make_dictproxy = lambda dictobj: type('',(),dictobj).__dict__

# for isinstance() check
dictproxy = type(make_dictproxy({}))


if __name__ == '__main__':

    import unittest
    
    class DictProxyTestCase(unittest.TestCase):
        
        def test_instance(self):
            d = make_dictproxy({})
            self.assertTrue(isinstance(d,dictproxy))
            self.assertFalse(isinstance(d.copy(),dictproxy))

        def test_get(self):
            d = make_dictproxy({'a': 'b', 'c': 'd'})
            self.assertEqual('b', d.get('a'))
            self.assertEqual('d', d.get('c'))
            
        def test_set(self):
            d = make_dictproxy({})
            def _test_item_assignment():
                d['a'] = 10
            self.assertRaises(TypeError, _test_item_assignment)
            
        def test_copy(self):
            d1 = make_dictproxy({'a': 'b', 'c': 'd'})
            d2 = d1.copy()
            self.assertEqual(d1, d2)
            self.assertTrue(isinstance(d2,dict))
            d2['a'] = 'z'
            self.assertNotEqual(d1, d2)
            self.assertEqual('z', d2['a'])
    
        def test_not_immutable(self):
            a = dict(val=20)
            b = make_dictproxy(a)
            a['val'] = 10
            self.assertEqual(10, b['val'])
            # This is ok, correct behavior.
            # So ... can/should not use dictproxy as immutable dict.
            # dictproxy just hide the interface.

    unittest.main()
    

I do not know why dictproxy is not in builtin or stdlib. I even wanted the type have create() classmethod or factory function. It may have some reasons to this type is hidden. I'm not sure about that.

After reading ctypes document few hours, I got the one-liner idea when I write this, just now. So I post ctypes version also, as sample of using pythonapi from Python...

Notes: multiprocessing, idellib has names DictProxy though, those are not about immutable dictionary.

Related Recipes:

Recipe 498072: Implementing an Immutable Dictionary http://code.activestate.com/recipes/498072/

1 comment

Ikkei Shimomura (author) 15 years, 5 months ago  # | flag

NOTE: dictproxy itself does not protect the dictionary data. it just hides the interfaces, It's not an immutable dict.

d = dictproxy({ ... })

a = dict(); b = make_dictproxy(a)

In the bottom case, user can change data via 'a'. And make_dictproxy() can not check that. Make ImmutableDict class is much safer.

Thx comments, the info from irc #ptyhon