Question

I am trying to add a new class to an existing class at run time (using "type(...)"). I am also trying to override that new class' __getattr__ so that I can do my own behavior on attributes that are not in the new class. For example, I have class foo, I add class "tool" and I want foo.tool.test to do something of my own. The code below works but only partly. If I explicitly call __getattr__, it works (see first print) but when I reference foo.tool.test, my overridden __getattr__ does not get called and an attrbute error is raised.

Your help is greatly appreciated.

class Foo(object):
    def __init__(self):
        self.NameList=[]
        # add new class to ourself
        self.tool = type('tool', (object,), {} )
        # override new class' __getattr__ with call to ourself
        setattr(self.tool, "__getattr__", self.__getattr__ )
        # add one well known test name for now
        self.NameList.append( "test" )

    # should be called by our newly added "tool" object but is only called sometimes...
    def __getattr__(self, attr):
        # print( "__getattr__: %s" % attr )
        if( attr in self.NameList ):     
            return( 99 )
        raise AttributeError("--%r object has no attribute %r" % (type(self).__name__, attr))       

foo = Foo()
# access tool class attribute "test" - it should be seen by the override __getattr__
# the following works...
print( "foo.tool.__getattr__=%d" % foo.tool.__getattr__("test") )  
# but the following does not - why is this not the same as the line above???
print( "foo.tool.test=%d" % foo.tool.test )                         
Was it helpful?

Solution

Python looks for special methods like __getattr__ in an instance's bases __dict__s, not in the instance's __dict__.

self.tool is a class. self.tool.test, therefore, will call the __getattr__ of self.tool's class (which is object) -- this is not what we want to happen.

Instead, make self.tool an instance, whose class has a __getattr__:

class Foo(object):
    def __init__(self):
        self.NameList=[]
        # add new class to ourself
        toolcls = type('tool', (object,), { '__getattr__' : self.__getattr__, } )
        self.tool = toolcls()
        self.NameList.append( "test" )

    # should be called by our newly added "tool" object but is only called sometimes...
    def __getattr__(self, attr):
        # print("__getattr__: (%s, %s)" % (self.__class__.__name__, attr) )
        if( attr in self.NameList ):     
            return( 99 )
        raise AttributeError("--%r object has no attribute %r" % (
            type(self).__name__, attr)) 

foo = Foo()
print( "foo.tool.__getattr__=%d" % foo.tool.__getattr__("test") )
print( "foo.tool.test=%d" % foo.tool.test )    

yields

foo.tool.__getattr__=99
foo.tool.test=99

Also, beware that the above code can lead to infinite recursion if an instance of Foo is made without self.NameList being defined. See Ned Batchelder's post on this suprising pitfall.

To protect against the possibility of infinite recursion here, use

def __getattr__(self, attr):
    # print("__getattr__: (%s, %s)" % (self.__class__.__name__, attr) )
    if attr == 'NameList':
        raise AttributeError()
    if( attr in self.NameList ):     
        return( 99 )
    raise AttributeError("--%r object has no attribute %r" % (
        type(self).__name__, attr)) 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top