1

I am trying to understand the concept of delegation in Python by using the getattr and setattr function . By basic idea is to first set the value of the attribute 'lang' in the Person class through the class Professional and then retrieve the same. The problem is that the result is an infinite loop.

class Person:
    def __init__(self,name='Subhayan',job='Engineer',unique='Unique'):
        print ("Inside init function of Person")
        self.name = name
        self.job = job
        self.salary = 50000
        self.lang = "Perl"

    def __setattr__(self,att,value):
        self.__dict__[att] = value


class Professional:
    job = 'Engineer'
    salary = 75000
    def __init__(self):
        print ("Inside Professional init function")
        self.person = Person()

    def __getattr__(self,attr):
        print ("Calling the getattr function")
        return getattr(self.person, attr)

    def __setattr__(self,att,value):
        # print ("calling the setattr function setting the value of %s to %s" %(attr,value))
        self.person.__setattr__(self,att,value)


if __name__ == '__main__':
    print ("Calling the script as main")
    Prof = Professional()
    print ("Salary is",Prof.salary)
    print ("name is",Prof.__class__)
    print ("Setting the value of lang")
    Prof.lang = 'Python'
    value = Prof.lang
    print ("The value of lang is ;",value)

2 Answers 2

4

__setattr__ is called for all attribute setting. This includes the self.person = Person() call in __init__:

def __init__(self):
    print ("Inside Professional init function")
    self.person = Person()

This'll call self.__setattr__('person', Person()), which in turn tries to access self.person, which then calls self.__getattr__('person') because there is no such attribute yet. And in __getattr__ you then end up in an infinite loop as you try to continually access self.person.

You could test for the specific person attribute in __setattr__ (and delegate that to the base implementation):

def __setattr__(self, att, value):
    # print ("calling the setattr function setting the value of %s to %s" %(attr,value))
    if att == 'person':
        return super().__setattr__(att, value)
    return self.person.__setattr__(self,att,value)

You probably also want to add a test in __getattr__; if it is called with person, then that attribute hasn't been set yet and an AttributeError should be raised:

def __getattr__(self,attr):
    print ("Calling the getattr function")
    if attr == 'person':
        raise AttributeError('person')
    return getattr(self.person, attr)
Sign up to request clarification or add additional context in comments.

Comments

3

The __setattr__ and __getattr__ also apply before your instance is fully initialized. In this case, your line self.person = Person(), calls __setattr__. This then calls __getattr__ (because self.person is not yet defined), which then recursively calls __getattr__ again (for the same reason).

There are a few ways around this. Perhaps the simplest is to circumvent the __setattr__ call on the initial self.person assignment by doing, for instance super().__setattr__('person', Person()).

In general, you need to be careful when using these methods, since they may be called more often than you realize. If your special handling applies to only a few certain attributes, you may want to use a property instead.

4 Comments

Ok so in that case i cannot initialize an object of Person from inside the init method of Professional
@SubhayanBhattacharya: Sure you can. Why do you think you can't?
I was thinking of replacing the call : self.person = Person() with something like : self.__dict__['person'] = Person(). This should not route calls to setattr. Am i correct ?
@SubhayanBhattacharya: Yes, you could do that as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.