Python Forum
Use of @property decorator - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: General Coding Help (https://python-forum.io/forum-8.html)
+--- Thread: Use of @property decorator (/thread-27490.html)

Pages: 1 2


Use of @property decorator - ruy - Jun-08-2020

Hi there,

I've noticed that when using the @property and @attribute_name.setter decorators all the examples I've found on the net always use an underscore before the variable name, even if in the __init__ they haven't. I tried to not use the underscore before the variable name and it doesn't work. Just wondring why?

Example code (temperature used in __init__ and _temperature in decorated methods)
# Using @property decorator
class Celsius:
    def __init__(self, temperature=0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    @property
    def temperature(self):
        print("Getting value...")
        return self._temperature

    @temperature.setter
    def temperature(self, value):
        print("Setting value...")
        if value < -273.15:
            raise ValueError("Temperature below -273 is not possible")
        self._temperature = value
Thanks a lot


RE: Use of @property decorator - Yoriz - Jun-08-2020

Without the underscore, the methods call themselves recursively until you hit the error
Error:
RecursionError: maximum recursion depth exceeded while calling a Python object
A different variable name is needed to store the variable then the method name.

For instance
@property
def temperature(self):
    print("Getting value...")
    return self.temperature
line 4 would call line 2 & then line 4 would call line 2 & then line 4 would call line 2 .........


RE: Use of @property decorator - buran - Jun-08-2020

single leading undescore means this is "for internal use only" variable.
This is defined in PEP8:
Quote:_single_leading_underscore: weak "internal use" indicator. E.g. from M import * does not import objects whose names start with an underscore.
Still, this is only convention - user has access to it, but the intention of the programmer is clear.
In your example you want to raise error when values is < -273.15. So, every time you do self.temperature the setter is called (the function decorated as @temperature.setter). Now you need to store the value somewhere (if it pass the test) and you use self._temperature.
Later when you try to access self.temperature, it will call the function decorated as @temperature.property and it will return self._temperature.
As I said, it's just a convention and one has access to _temprature, but really should not work directly with it

# Using @property decorator
class Celsius:
    def __init__(self, temperature=0):
        self.temperature = temperature
 
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32
 
    @property
    def temperature(self):
        print("Getting value...")
        return self._temperature
 
    @temperature.setter
    def temperature(self, value):
        print("Setting value...")
        if value < -273.15:
            raise ValueError("Temperature below -273 is not possible")
        self._temperature = value

celsius = Celsius(100)
print(celsius.temperature)
celsius._temperature = -1000
print(celsius.temperature)
celsius.temperature = -300
Output:
Setting value... Getting value... 100 Getting value... -1000 Setting value... Traceback (most recent call last): File "/home/boyan/sandbox2/forum.py", line 25, in <module> celsius.temperature = -300 File "/home/boyan/sandbox2/forum.py", line 18, in temperature raise ValueError("Temperature below -273 is not possible") ValueError: Temperature below -273 is not possible



RE: Use of @property decorator - stullis - Jun-08-2020

The underscore differentiates the field from the methods. Without the underscore, Celsius.temperature will always call one of the methods instead of the variable and it ends up in an infinite recursion loop. The Celsius.temperature() methods would just keep calling themselves ad infinitum.

To walk through this particular example, Celsius:
  1. Celsius.__init__(5) sets self.temperature to 5 by calling the Celsius.temperature() setter.
  2. Celsius.temperature() creates the self._temperature field and sets it to 5.
  3. Celsius.to_fahrenheit() calls the Celsius.temperature() getter.
  4. Celsius.temperature() returns the value of self._temperature.
  5. Celsius.to_fahrenheit() uses the result of Celsius.temperature() to calculate the Fahrenheit value and returns it.



RE: Use of @property decorator - ruy - Jun-08-2020

Thanks Yoriz,

And how does python return self._temperature? That instance attribute was never definied. The interpreter actually returns self.temperature. And I noticed the in the code I posted the object ends up with two attributes: temperature and _temperature. I noticed also that which ever one I update the other one updates also.Isn't that a waste of memory? Or are the 2 attributes pointg to the same memory?


RE: Use of @property decorator - buran - Jun-08-2020

(Jun-08-2020, 06:01 PM)ruy Wrote: And how does python return self._temperature?
(Jun-08-2020, 05:37 PM)buran Wrote: Later when you try to access self.temperature, it will call the function decorated as @temperature.property and it will return self._temperature.



RE: Use of @property decorator - ruy - Jun-08-2020

Yes my bad the self._temperature was defined in the setter. So in this example I end with the two attributes, one with the underscore one without. The one without being the one I should use to interact with the object and the one with the underscore for internal use only. Correct? Is there any performance penalties for having these two attributes instead of one?

Thanks everyone


RE: Use of @property decorator - Yoriz - Jun-08-2020

class Celsius:
    def __init__(self, temperature=0):
        self.temperature = temperature
This is not assigning temperature to self.temperature
its calling the property setter method temperature which inside creates the variable self._temperature = value


RE: Use of @property decorator - deanhystad - Jun-08-2020

So what is the accepted practice for properties? I've read (and been warned by lint) that it is bad form to define instance variables outside __init__. But so many property examples show the property setter used to create the _variable. If this is ok:
def __init__(self):
    self.variable = 5

@property
def variable(self):
    return self._variable

@variable.setter
def variable(self, value):
    self._variable = value
why is this bad:
def __init(self)__:
    self.reset()

def reset(self):
    self.scale = 1
    self.offset = 0



RE: Use of @property decorator - buran - Jun-09-2020

Actually, this:
def __init__(self):
    self.variable = 5
 
@property
def variable(self):
    return self._variable
 
@variable.setter
def variable(self, value):
    self._variable = value
is NOT OK. You don't need to use setter and property in this case. You don't do anything additional in the setter and/or property. In your original code you were checking the value and raising error if not accepted value.

Example for OK:
from math import pi 

class Circle:
    def __init__(self, radius):
        self.radius = radius

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

    @property
    def diameter(self):
        return self.radius * 2

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



circle = Circle(2)
print(f'circumference:{circle.circumference}')
print(f'diameter: {circle.diameter}')
circle.diameter = 8
print(f'radius: {circle.radius}')
print(f'circumference: {circle.circumference}')
Note - it's possible to complicate the example by allowing user to define Circle by radius OR diameter in __init__

The second one is fine in my opinion as long as reset() is really needed and used later/elsewhere in the code or as instance method.