Welcome to a tutorial on @property Decorator in Python.
The @property is a built-in decorator in Python, that is typically used in making functions such as getters and setters in a class behave like a class property. Check out the example below, for a better understanding.
In the example below, there is a class student, that has 3 properties, which are: fname, lname, fullname. The fullname is a concatenation of the first two properties. Also, there is a function, the email() function, which is used to generate an email address for the student using its first and last names.
class Student:
def __init__(self, fname, lname):
self.fname = fname
self.lname = lname
self.fullname = self.fname +' '+ self.lname
# generate email using first and last name
def email(self):
return '{}.{}@tutorialwithexample.com'.format(self.fname, self.lname)
Now, we will create a few objects and call the function email().
# student s1
s1 = Student('Mike', 'Steve')
print('Fullname of s1 is ', s1.fullname)
print('And email address = ', s1.email())
# now updating the s1 object's first name
s1.first = 'Jemes'
print('Fullname of s1 is ', s1.fullname)
print('And email address = ', s1.email())
Output:
Fullname of s1 is Mike Steve
And email address = [email protected]
Fullname of s1 is Tony Stark
And email address = [email protected]
From the example above, in-class students, fname(first name) and lname (last name) are simple attributes and are not derived from any other attributes. But, fullname and email() are the derived attributes. This is because, fullname was declared as a variable, while the email was declared as a function.
In the output, you can see that for a student when the first name is changed then the email gets changed automatically but the fullname doesn't change because email() is a function that is called at the time when we want the email to be returned, but the fullname is set at the time of initialization of the instance or object.
Now, suppose, there is a need to fix the problem for fullname; this can be done by making a function similar to email to get fullname too. However, it will lead to the overhead of changing all the access made to fullname in all the python files that have used the Student class.
In using the @property in the example above to solve the problem of fullname by creating a getter function for fullname that will allow it to be used as a simple variable as well as return the updated value of fullname property.
Check out the example below:
class Student:
def __init__(self, fname, lname):
self.fname = fname
self.lname = lname
@property
def fullname(self):
return self.fname +' '+ self.lname
# generate email using first and last name
def email(self):
return '{}.{}@tutorialwithexample.com'.format(self.fname, self.lname)
s1 = Student('Tony', 'Stark')
print('Fullname of s1 is ', s1.fullname)
print('And email address = ', s1.email())
# now updating the s1 object's first name
s1.first = 'Steve'
print('Fullname of s1 is ', s1.fullname)
print('And email address = ', s1.email())
Output:
Fullname of s1 is Tony Stark
And email address = [email protected]
Fullname of s1 is Steve Stark
And email address = [email protected]
From the example above, the @property decorator is used on the function named fullname(). Also, this function will work as a fullname attribute and can work as a getter, because of the @property decorator attached to it.
Just like the getter method, we can define a setter and deleter methods for any attribute that is using @property.
In Python, the setter method sets the value of the attribute, while the deleter method will delete the attribute from the memory.
Now, we create a code in which we will implement a setter and deleter method for our fullname attribute. To define a setter and deleter method for an attribute with @property decorator set on its getter method, a special syntax is used, in which we put @ATTRIBUTE_NAME.setter and @ATTRIBUTE_NAME.deleter decorator on function with name same as the ATTRIBUTE_NAME, check out the example below.
class Student:
def __init__(self, fname, lname):
self.fname = fname
self.lname = lname
@property
def fullname(self):
return self.fname +' '+ self.lname
#setter for the fullname
@fullname.setter
def fullname(self, name):
# split the name from space
fname, lname = name.split(" ")
self.first = fname
self.last = lname
#deleter for fullname
@fullname.deleter
def fullname(self):
self.first = None
self.last = None
print('Deleted the fullname')
# generate email using first and last name
def email(self):
return '{}.{}@tutorialwithexample.com'.format(self.fname, self.lname)
s1 = Student('Tony', 'Stark')
print('Fullname of s1 is ', s1.fullname)
print('And email address = ', s1.email())
# now updating the s1 object's first name
s1.first = 'Steve'
print('Fullname of s1 is ', s1.fullname)
print('And email address = ', s1.email())
#setting new value of fullname
s1.fullname = 'Berry Banner'
print('New Fullname of s1 is ', s1.fullname)
#deleting the fullname
del s1.fullname
Output:
Fullname of s1 is Tony Stark
And email address = [email protected]
Fullname of s1 is Steve Stark
And email address = [email protected]
New fullname of s1 is Berry Banner
Deleted the fullname.
In this case, it is best to use the property() function in python to create getters, setters, and deleters rather than the @property decorator.
The basic Syntax are:
property(fget, fset, fdel, doc)
fget(): Is used to get the value of an attribute
fset(): Is used to set the value of an attribute
fdel() : Is used to delete the attribute value
doc(): It is a string that contains the documentation (docstring) for the attribute
return: This typically returns a property attribute from the given getter, setter, and deleter.
Check out the example below: It illustrates the application of the property() function for the same class student with the same functions defined as a getter, setter and delete.
class Student:
def __init__(self, fname, lname):
self.fname = fname
self.lname = lname
self.fullname = fname+' '+lname
def fullname_getter(self):
return self.fname +' '+ self.lname
def fullname_setter(self,name):
firstname, lastname = name.split()
self.fname = firstname
self.lname = lastname
def fullname_deleter(self):
self.fname = None
self.lname = None
print('Deleted the fullname.')
def email(self):
return '{}.{}@email.com'.format(self.fname, self.lname)
fullname = property()
fullname = fullname.getter(fullname_getter)
fullname = fullname.setter(fullname_setter)
fullname = fullname.deleter(fullname_deleter)
# this can be done in a single line too
# fullname = property(fullname_getter, fullname_setter, fullname_deleter)
s1 = Student('Tony', 'Stark')
print('Fullname of s1 is ', s1.fullname)
print('And email address = ', s1.email())
# now updating the s1 object's first name
s1.first = 'Steve'
print('Fullname of s1 is ', s1.fullname)
print('And email address = ', s1.email())
#setting new value of fullname
s1.fullname = 'Berry Banner'
print('New Fullname of s1 is ', s1.fullname)
#deleting the fullname
del s1.fullname
Output:
Fullname of s1 is Tony Stark
And email address = [email protected]
Fullname of s1 is Steve Stark
And email address = [email protected]
New fullname of s1 is Berry Banner
Deleted the fullname.
From the example above, the property() function can either be used to specify all the getter, setter and delete functions as parameters if making use of the syntax described above, as well as multiple code lines.