Reputation: 5705
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)
Upvotes: 1
Views: 7731
Reputation: 251345
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.
Upvotes: 3
Reputation: 1121186
__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)
Upvotes: 4