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 = valueThanks a lot RE: Use of @property decorator - Yoriz - Jun-08-2020 Without the underscore, the methods call themselves recursively until you hit the error 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.temperatureline 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
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:
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 = temperatureThis 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 = valuewhy 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 = valueis 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. |