__getattr__ and recursion ?


S

Stef Mientki

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 ??

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 []
# ***********************************************************************
 
Ad

Advertisements

P

Peter Otten

Stef said:
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 []
# ***********************************************************************
 
Ad

Advertisements

S

Stef Mientki

thanks Peter,

for your perfect explanation, and
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__().
yes, that's probably much better, have to get used to this Python behavior,
thanks,

cheers,
Stef
 

Top