1

I am learning how to create classes in python where when modifying attribute 1 will change attribute 2. For instance a circle with an attribue radius.

class Circle():
    def __init__(self,radius):
        self._radius = radius
        
    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        self.radius = value
    
    @property
    def area(self):
        return 3.14*self.radius*self.radius
    

And this happens:

c = Circle(3)
c.area

All fine. but now:

c.radius = 56
print(c.area)

gives:

RecursionError: maximum recursion depth exceeded

The question is why? What is the way to force recalculation of other attributes when one is changed?

I have consulted several answers: Python: modifying property value How do you change the value of one attribute by changing the value of another? (dependent attributes)

EDIT: According to some answers (see bellow) I am in an infinite loop. So I delete the part of the setter. I remain with:

class Circle():
    def __init__(self,radius):
        self._radius = radius
        
    @property
    def radius(self):
        return self._radius
    
    @property
    def area(self):
        return 3.14*self.radius*self.radius
    

What happens then is:

c = Circle(3)
c.radius = 30

error: AttributeError: can't set attribute

JFerro
  • 3,203
  • 7
  • 35
  • 88
  • 2
    You've told Python that you set `radius` by setting `radius`, which you do by setting `radius`, which you do by setting `radius`, which you do by setting `radius`... – user2357112 Feb 03 '22 at 11:55
  • 2
    Because the code for setting `self.radius` invokes the code for setting `self.radius`. You probably meant `self._radius = value`. – timgeb Feb 03 '22 at 11:55
  • thanks for the answers. I tried to solve it by deleting the setter, and it did not work I got an error as seen in the EDIT part of the question. – JFerro Feb 03 '22 at 12:51
  • 2
    Why did you delete the setter? The solution is to use `self._radius = value` in the setter. – timgeb Feb 03 '22 at 14:35

1 Answers1

0

After the comments posted here this is the answer with exploratory explanation:

class Circle():
    def __init__(self, _radius, color):
        print('init run')
        self._radius = _radius
        self.color = color
        
    @property
    def radius(self):
        print('property radius run')
        return self._radius
    
    @radius.setter
    def radius(self, value):
        print('setter radius run')
        self._radius = value
    
    @property
    def area(self):
        print('property AREA run')
        return 3.14*self.radius*self.radius
    

I added print statements because if you want to find out what really happens I recommend you to run this code in separate cells in a notebook:

c= Circle(3,'azul') # here only __init__ runs
print(c.radius) # here property radius run

print(c.area) 

This is the interesting part. When printing c.area things happen. Before running the code think about what do you think it would happenI was surprised but relieved because I understood now the functioning. This is what happens: property AREA run, property radius run, property radius run. My lessons learnt: Accessing the radius property is done via the method. This WAS NOT AT ALL OBVIOUS FOR ME.

Now try to change the radius

c.radius = 56 # setter method runs

print(c.area)   # the same ass before. 
JFerro
  • 3,203
  • 7
  • 35
  • 88