__getattr__ and recursion ?

Peter Otten __peter__ at web.de
Wed Apr 30 06:14:08 EDT 2008


Stef Mientki wrote:

> hello,
> 
> I tried to find an easy way to add properties (attributes) to a number
> of different components.
> So I wrote a class, from which all these components are derived.
> By trial and error I created the code below, which now works, but
> there is one thing I don't understand:
> in the line indicated with "<<== 1" I'm not allowed to use
> 
>             for item in self.extra_getters :
> 
> because it will result in an infinite recursion.
> But in the line indicated with "<<== 2" , I am allowed ...
> ... why is this allowed ??

When the instance is created 

self.extra_setters = {} 

in the __init__() method triggers

self.__setattr__("extra_setters", {})

which executes

for item in self.extra_setters:
   # ...

in the __setattr__() method. Because at that point there is no extra_setters
attribute 

self.__dict__["extra_setters"] 

fails and self.__getattr__("extra_setters") is used as a fallback. Now as
__getattr__() contains a self.extra_getters attribute access and that
attribute doesn't exist either this again triggers 

self.__getattr__("extra_getters") -- ad infinitum.

By the way, looping over a dictionary destroys its key advantage, O(1)
lookup. Use

# untested
if attr in self.extra_setters:
    self.extra_setters[attr](value)
else:
    self.__dict__[attr] = value

and something similar in __getattr__().

Peter

> 
> thanks,
> Stef Mientki
> 
> 
> # ***********************************************************************
> # ***********************************************************************
> class _add_attribs ( object ) :
>     def __init__ ( self ) :
>         self.extra_setters = {}
>         self.extra_getters = {}
> 
>     def _add_attrib ( self, text, setter = None, getter = None ) :
>         if setter :
>             self.extra_setters [ text ] = setter
>         if getter :
>             self.extra_getters [ text ] = getter
> 
>     # *********************************************************
>     # always called instead of the normal mechanism
>     # *********************************************************
>     def __setattr__ ( self, attr, value ) :
>         for item in self.extra_setters :
>             if item == attr :
>                 self.extra_setters [ item ] ( value )
>                 break
>         else :
>             self.__dict__[attr] = value
> 
>     # *********************************************************
>     # only called when not found with the normal mechanism
>     # *********************************************************
>     def __getattr__ ( self, attr ) :
>         try :
>             for item in self.__dict__['extra_getters'] :            <<== 1
>                 if item == attr :
>                     return self.extra_getters [ item ] ( )          <<== 2
>       except :
>           return []
> # ***********************************************************************




More information about the Python-list mailing list