Properties are especially useful when you want to access attributes of an object directly and need to have some extra code around getters/setters in some cases.
Imagine you have a car object.
class Car(object):
def __init__(self, make, model, year, vin):
self.make = make
self.model = model
self.year = year
self.vin = vin
my_car = Car('Ford', 'Taurus', 2005, '123ABCVINSARELONG999')
Now imagine that I need to change the year on the car. I might do it a couple different ways.
my_car.year = 2006
my_car.year = '2007'
The second way gives me some serious problems if I'd like to assume the year is a number. See the following:
if(my_car.year > 2010):
print('Shiney new!')
The challenge is that setting attributes directly is very clean and slick, but is not providing us an opportunity to do any data validation (maybe I want to ensure the make is in a list of known makes, for example) or data type conversion (in the year example).
Properties come to the rescue by allowing us to setup wrapper code around attribute assignment and/or retrieval but not requiring that we do that for all attributes or even requiring us to know ahead of time that we want to do some extra validation. I can, after the fact, modify my Car object as follows and it would still be used in exactly the same way.
class Car(object):
def __init__(self, make, model, year, vin):
self.make = make
self.model = model
self._year = year
self.vin = vin
def get_year(self):
return self._year
def set_year(self, val):
self._year = int(val)
year = property(get_year, set_year)
You still access year as my_car.year and you still set it with my_car.year = 2006, but it now converts the value into an int where necessary and throws an exception if the value provided by the user doesn't convert into an int properly.