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.