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

some while ago, i posted a recipe with linked dictionaries (see <a href='http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/465748'>#465748</a>) to this site. yesterday a use case came up where that kind of delegational name/value lookup would be a nice thing if it worked for instance attributes.

basically, using a customized dictionary as an instance <tt>__dict__</tt> means modifying the standard lookup-by-inheritance-only behavior; in other words, part of the state of an instance can be kept in another instance.

this way, an instance that holds, say user configuration data, <tt>userCfg</tt> can be linked, at runtime, to a <tt>defaultCfg</tt> instance, and all the <tt>userCfg.frobfactor</tt> stuff that has not been defined by the user but for the default configuration would automatically be available.

unfortunately, the naïve approach (setting <tt>self.__dict__=MyDict()</tt> in <tt>__init__</tt>) fails to work. an inquiry to comp.lang.python was swiftly answered by bengt richter, who suggested to use a property for <tt>__dict__</tt>. i slightly improved on his suggestion by adding the capability to do item assignment as well.

note that since we are using <tt>__getattr__</tt> to redirect attribute lookup, delegation only happens <i>after</i> lookup in the method resolution order (mro) has failed (inheritance overrides delegation).

Python, 76 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
################################################################################
#
#
#
#===============================================================================
class CustomDict( dict ):

    #---------------------------------------------------------------------------
    defaultValue = 'THIS ITEM NOT AVAILABLE'

    #---------------------------------------------------------------------------
    def __getitem__( self, name ):
        try:
            return super( CustomDict, self ).__getitem__( name )
        except KeyError:
            return self.defaultValue

    #---------------------------------------------------------------------------
    def __contains__( self, name ):
        return True

    #---------------------------------------------------------------------------
    def has_key( self, name ):
        return True

################################################################################
#
#
#
#===============================================================================
class X( object ):

    #---------------------------------------------------------------------------
    def __init__( self ):
        self._dict = CustomDict( foo = 'bar' )

    #---------------------------------------------------------------------------
    @property
    def __dict__( self ):
        #print 'X.__dict__ ( get() )'
        return self._dict

    #---------------------------------------------------------------------------
    def __getattr__( self, name ):
        return self.__dict__[ name ]

    #---------------------------------------------------------------------------
    def __setattr__( self, name, value ):
        if name == '_dict':
            return super( X, self ).__setattr__( name, value )
        self._dict[ name ] = value


################################################################################
#
#
#
#===============================================================================
if __name__ == '__main__':

    x = X()
    print x.__dict__[ 'foo' ]
    print x.__dict__[ 'bar' ]
    print x.foo
    print x.bar
    print x.__dict__
    x.oops = 42
    print x.__dict__

#   Output:
#	    bar
#	    THIS ITEM NOT AVAILABLE
#	    bar
#	    THIS ITEM NOT AVAILABLE
#	    {'foo': 'bar'}
#	    {'foo': 'bar', 'oops': 42}

this appears to work so far. i am clueless as to why, tho. my suspicion is that, under the hood, the python vm does some kind of shortcut that, in effect, cancels a special class's behavior when used as an instance state dictionary.

Created by Wolfgang Lipp on Wed, 25 Jan 2006 (PSF)
Python recipes (4591)
Wolfgang Lipp's recipes (2)

Required Modules

  • (none specified)

Other Information and Tasks