I'm coming from C# world into python. In C#, I can use get
and set
to make updates to other object properties upon setting a value to a property, something like this:
private int _age;
private string _description;
public int Age
{
set
{
_age = value;
_description = (_age < 1500) ? "new": "old";
}
}
In python, I have the following class definition:
class Record(object):
age = 0
description = ""
<...a bunch of other properties...>
How can I set the description
property of a Record
object when I set the age
property, in the same way as I can do it in C#, without having to write custom functions like get_age(self):
and set_age(self):
?
You should use the property
decorator for this:
class Record:
def __init__(self):
self._age = 0
self._description = ""
@property
def age(self):
return self._age
@age.setter
def age(self, value):
self._age = value
self._description = "new" if value < 1500 else "old"
@property
def description(self):
return self._description
rec = Record()
print(rec.age, rec.description)
rec.age = 1000
print(rec.age, rec.description)
rec.age = 2000
print(rec.age, rec.description)
output:
0
1000 new
2000 old
When you use @property
, every time you use rec.description
, you are actually calling rec.description()
and the return value of that call is used as the attributes value. This value gets recalculated every time you use the attribute.
If there was only the @property
decorator, it would be impossible to set the attribute, as there is only a function that calculates the value. Luckily, we can use @age.setter
. Every time the attribute is set (rec.age = value
), this function is called.
We can use of this to make the description
attribute also being set when the age
attribute is set.
If you want to learn more about decorators, this page is a good start.
Another, less recommended, way to do this is using __setattr__
:
class Record:
def __init__(self):
self.age = 0
self.description = ""
def __setattr__(self, key, value):
super().__setattr__(key, value)
if key == "age":
self.description = "new" if value < 1500 else "old"
rec = Record()
print(rec.age, rec.description)
rec.age = 1000
print(rec.age, rec.description)
rec.age = 2000
print(rec.age, rec.description)
The output is exactly the same.
Every time we set an attribute on an instance by using instance.attrname = attrvalue
, python calls instance.__setattr__("attrname", attrvalue)
. Usually, this is the __setattr__
function which is inherited from object
, the class from which every class inherits.
But when we override this function, we can choose our own behavior. In this case, it means that we also set the description
attribute when the attribute 's name is "age"
.
But, of course, we should still call the object
's __setattr__
function to make that the attribute is actually set. We could do this directly using object.__setattr__(self, key, value)
, but a better way to do this is using super
. super()
allows us to call functions of the class from which there is inherited. In this case it's object
, but if you later on decide to let Record
inherit from another class that also defines __setattr__
, that class's __setattr__
will be called.