Property allows to define getter and setters for attributes, which are called implicitly when the attribute is accessed or modified (without requiring a function call from the user/client)
By using property, attributes of a class can be modified while being backward compatible
Consider following class being implemented to store user data
class User:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
self.full_name = self.first_name + ' ' + self.last_name
Consider the use-case when the last_name of a person is changed
>>># create new object
>>> user = User('Ferbina', 'Miller', 22)
>>> user.full_name
Ferbina Miller
>>> user.age
22
>>> user.last_name = 'Darwin'
>>> user.last_name
Darwin
>>> user.full_name
Ferbina Miller
Since, in the above example, full_name is only set initially, it does not get changed when user changes last_name
To mitigate this, full_name can be changed into a function which returns the full name based on first_name and last_name
class User():
def __init__(self, first_name, last_name):
self.first = first_name
self.last = last_name
def full_name(self):
return self.first + ' '+ self.last
But this can break code which uses the initial class since code using full_name
attribute now have to call full_name()
method
Anyone depending on it can be told "this is the way things are now", or @property
decorator can be used to allow backward compatibility
@property
decorator A method can be converted to a property by adding a @property
decorator before the methodβs definition
This makes the method seem & function like an attribute
class User():
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
# Adding @property provides the updated full_name, which gets called when full_name is accessed like an attribute
@property
def full_name(self):
return self.first_name + ' '+ self.last_name
user = User('Ferbina', 'Miller')
print(user.__dict__)
user.last_name = 'Darwin'
print(user.__dict__)
Output
{'first_name': 'Ferbina', 'last_name': 'Miller'}
{'first_name': 'Ferbina', 'last_name': 'Darwin'}
This enables the method full_name()
to be accessible as attribute full_name
property()
method In Python, property()
is a built-in function that creates and returns a property object
The syntax of this function is
property(fget=None, fset=None, fdel=None, doc=None)
where, fget
is the function to get value of the attribute, fset
to set value, fdel
to delete the attribute and doc
is a string (like a comment)
These function arguments are optional, and can be set later by the methods, getter()
, setter()
, and deleter()
to specify fget
, fset
and fdel
Functions setter() and deleter() can also be assigned as decorator
Since the above example doesn't user setter method, attempting to set full_name
raises AttributeError
>>>user.full_name = 'Derpina Darwin'
Traceback (most recent call last):
File "property2.py", line 17, in <module>
user.full_name = 'Derpina Darwin'
AttributeError: can't set attribute
@<property-name>.setter
decorator can be used to assign setter for a property, where <property-name>
is the name of property (full_name
above)
class User():
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
@property
def full_name(self):
return self.first_name + ' ' + self.last_name
@full_name.setter
def full_name(self, name):
self.first_name, self.last_name = name.split(" ")
p1 = User("Ferbina", "Miller")
print(p1.__dict__)
p1.full_name = "Ferbina Darwin"
print(p1.__dict__)
Output
{'first_name': 'Ferbina', 'last_name': 'Miller'}
{'first_name': 'Ferbina', 'last_name': 'Darwin'}
@<property-name>.deleter
decorator can be used to assign deleter method for a property
class User():
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
@property
def full_name(self):
return self.first_name + ' ' + self.last_name
@full_name.setter
def full_name(self, name):
self.first_name, self.last_name = name.split(" ")
@full_name.deleter
def full_name(self):
self.first_name = None
self.last_name = None
p1 = User("Ferbina", "Miller")
print(p1.__dict__)
del p1.full_name
print(p1.__dict__)
Output
{'first_name': 'Ferbina', 'last_name': 'Miller'}
{'first_name': None, 'last_name': None}