Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Use of @property decorator
#1
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
Reply
#2
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 .........
Reply
#3
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
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#4
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.
Reply
#5
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?
Reply
#6
(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.
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#7
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
Reply
#8
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
Reply
#9
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
Reply
#10
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.
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  the order of running code in a decorator function akbarza 2 526 Nov-10-2023, 08:09 AM
Last Post: akbarza
  Curious about decorator syntax rjdegraff42 14 2,109 May-03-2023, 01:21 PM
Last Post: rjdegraff42
  Subclass initialized property used in parent class method. Is it bad coding practice? saavedra29 5 1,798 Feb-07-2022, 07:29 PM
Last Post: saavedra29
  ABC Module and @property decorator, Pythonic Way? muzikman 21 5,681 Aug-18-2021, 06:08 PM
Last Post: muzikman
  @property vs __set__ / __get__ and __setattr__ / __getattr__ okhajut 1 3,342 Jun-15-2021, 03:48 PM
Last Post: snippsat
  Can property getters and setters have additional arguments? pjfarley3 2 3,048 Oct-30-2020, 12:17 AM
Last Post: pjfarley3
  decorator adamfairhall 0 1,566 Aug-18-2020, 08:38 AM
Last Post: adamfairhall
  Property price calculation oli_action 4 3,172 Jul-15-2020, 04:27 PM
Last Post: sridhar
  Problem adding keys/values to dictionary where keynames = "property" and "value" jasonashaw 1 2,053 Dec-17-2019, 08:00 PM
Last Post: jasonashaw
  strange class property KaliLinux 2 2,362 Nov-25-2019, 04:32 PM
Last Post: KaliLinux

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020