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).
| 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.

 Download
Download Copy to clipboard
Copy to clipboard