On 25 Jan 2006 09:35:50 -0800, "[EMAIL PROTECTED]" <[EMAIL PROTECTED]> wrote:
>thx! indeed, it worked -- took me some time to figure out how to >implement the setting of attributes, too. i finally managed to get that >done using super: Seems good. <snip> > >the ``~.__setattr__()`` method tests for the special name ``_dict`` and >defers execution to the super of the ``X`` instance, ``object``. other >stuff is handled by the instance itself. seems a clean way to do it. >testing that with :: > > 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__ > >yields :: > > bar > THIS ITEM NOT AVAILABLE > bar > THIS ITEM NOT AVAILABLE > {'foo': 'bar'} > {'foo': 'bar', 'oops': 42} > >as expected. > >i tried to reason *why* the usage of a property makes this particular >piece of code work, but i seemingly can't find out. anyone around who >has thoughts on that? > I had an inspiration and I think succeeded in doing what you were originally trying to do (replace the actual instance dict with your custom dict), which I tried to do but didn't realize at first that the __init__ setting of x.__dict__ was really setting x.__dict__['__dict__'] not setting the initial x.__dict__ itself. __dict__ is a peculiar animal, and looking for an instance's attribute dict doesn't start at the instance. If you look for instance.__dict__, it is just like looking for any other attribute, and it starts at type(instance).mro()[0] looking for a descriptor (which a method also is). But the first thing "found" is a dict proxy for looking up attributes, and when it looks up '__dict__' it returns a descriptor, which then gets its __get__ method called with the instance whose '__dict__' is being sought. You have to use the corresponding __set__ method to set the value of '__dict__' (in this case the CustomDict instance). Otherwise instance.__dict__ will automatically be set to {} and then used so you have {'__dict__':CustomDict()} instead of CustomDict() itself. Once this is initialized correctly, then all the machinery works, except we still need to intercept __getattr__ for some reason. I suspect this is another symptom of some optimization. One of these days I will have to look in the source ;-) You would think it could give up on the mro method search and get the __dict__ normally to get the attribute, but some mechanism is not finding the custom dict, or else maybe it's bypassing the __getitem__ and going directly to the base dict method. Someday I'll have to look in the source ;-) customdict as before >>> 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 __getattr__(self, attr): ... return self.__dict__[attr] ... #return type(self).__dict__['__dict__'].__get__(self)[attr] ... def __init__( self, *args, **kw ): ... type(self).__dict__['__dict__'].__set__(self, CustomDict(*args, **kw) ... >>> x = X(foo='bar') >>> print x.__dict__['foo'] bar >>> print x.__dict__['bar'] THIS ITEM NOT AVAILABLE >>> print x.foo bar >>> print x.bar THIS ITEM NOT AVAILABLE >>> x.oops = 42 >>> print x.__dict__ {'foo': 'bar', 'oops': 42} Looking at a few things of interest: >>> vars(x) {'foo': 'bar', 'oops': 42} >>> type(vars(x)) <class '__main__.CustomDict'> >>> type(x.__dict__) <class '__main__.CustomDict'> >>> vars(x)['?'] 'THIS ITEM NOT AVAILABLE' >>> type(x) <class '__main__.X'> >>> type(x).__dict__ <dictproxy object at 0x02E81554> >>> type(x).__dict__['__dict__'] <attribute '__dict__' of 'X' objects> >>> type(x).__dict__['__dict__'].__get__ <method-wrapper object at 0x02EF3B6C> >>> type(x).__dict__['__dict__'].__get__(x) {'foo': 'bar', 'oops': 42} >>> type(type(x).__dict__['__dict__'].__get__(x)) <class '__main__.CustomDict'> The reason the property was needed before was really several reasons. First was that x.__dict__ wasn't being set properly, so the internal machinery wasn't finding the custom dict. I changed the name to _dict and let it be an ordinary attribute, but then used property to fake what normal stuff would do if x.__dict__ itself was set, not x.__dict__['__dict__'] Wasted a bunch of time trying to get rid of that __getattr__ ;-/ Regards, Bengt Richter -- http://mail.python.org/mailman/listinfo/python-list