newb question about @property

Steve D'Aprano steve+python at pearwood.info
Mon Oct 2 20:00:24 EDT 2017


On Tue, 3 Oct 2017 06:32 am, Bill wrote:

> Steve D'Aprano wrote:
>> Circle didn't use any setters, but I could have let you set the
>> diameter, which in
>> turn would set the radius:
>>
>> circle.radius = 2
>> assert circle.diameter == 4
>> circle.diameter == 2  # requires a setter
>> assert circle.radius == 1
>>
>> Getting that to work is left as an exercise :-)
>>
> It WAS a good exercise!!  I was concerned about "infinite recursion"
> between my two property setters..  Thanks!   Next?  :)
> 
> Bill
> 
> 
> import math
> 
> 
> class Circle(object):
>      """ Define a circle class with radius and diameter"""
>      def __init__(self, radius):
>          self.radius = radius
>          self.diameter =2*radius

There's no need to set the radius and the diameter, as one is completely derived
from the other and the transformation is cheap enough to perform on the fly as
needed.

Consider what happens if, due to a bug or an accident, you end up with a Circle
instance that says the radius is 5 and the diameter is 20. They can't *both* be
right, and you will get inconsistent results depending on whether your other
methods happen to use the diameter or the radius.

Instead, we should steal the Single Source Of Truth principle from informations
systems theory:

https://en.wikipedia.org/wiki/Single_source_of_truth

https://en.wikipedia.org/wiki/Single_version_of_the_truth

There are valid cases for violating this principle in object design, e.g. caches
are technically a violation of SSOT but an acceptable one.

With that in mind, your class simplifies to:

class Circle(object):
    """ Define a circle class with radius and diameter"""
    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 diameter(self):
        return 2*self._radius

    @diameter.setter
    def diameter(self, value):
        self._radius = value/2

    @property
    def area(self):
        return math.pi*self.radius**2

    @property
    def circumference(self):
        return math.pi*self.diameter



-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list