Introduction

In Python, properties are a way to control access to class attributes. They allow you to add customized logic to the attribute access and modification operations of your classes, making your code more flexible and easier to maintain. The use of property decorators in Python is becoming more and more important every day, as programmers need to design programs that are more complex and efficient. In this tutorial, we'll cover everything you need to know about property decorators in Python.

Table of Contents :

  • What are property decorators in Python
  • Attributes of Classes
  • The Getter and Setter methods
  • Managing class attributes in Python
    • Creating getter and setter methods of a class
    • Using property() as a Decorator
    • Creating Read-Only Attributes
    • Creating write-only Attributes
    • Creating Read-Write Attributes
    • Creating computed Attributes
    • Caching computed Attributes
    • Managing Attribute Deletion
    • Validating Input Values
    • Logging Attribute behavior
    • Overriding parent class behavior
    • Creating Backward-Compatible Class APIs

What are property decorators in Python :

  • Property decorators are used to add functionality to properties of classes. 
  • We'll start by defining a class with a property: 
  • Code Sample : 
class Person: 
	def __init__(self, name): 
		self._name = name 

@property 
def name(self): 
	return self._name 
	
	
	
  • We've defined a simple class with a property called name. The name property is just a getter that returns the value of the  _name  attribute. 
  • Now, let's say we want to add a setter to the name property. We can do this with a property decorator: 
  • Code Sample : 

class Person: 
	def __init__(self, name): 
		self._name = name 

@property 
def name(self): 
	return self._name 


@name.setter 
def name(self, value): 
	self._name = value 
	
	
	
  • Now, the name property has both a getter and a setter. We can use the setter to set the value of the  _name attribute: 
  • Code Sample : 

p = Person('John') 
p.name = 'Bob' 
print(p._name) 
# Bob 

We can also use the getter to get the value of the _name attribute: 

print(p.name) 
# Bob 


  • In addition to the setter, we can also define a property decorator for the deleter: 
  • Code Sample : 

class Person: 
def __init__(self, name): 
self._name = name 

@property 
def name(self): 
return self._name 

@name.setter 
def name(self, value): 
self._name = value 

@name.deleter 
def name(self): 
del self._name 

  • Now, we can delete the  _name  attribute with the deleter: 
  • Code Sample : 

p = Person('John') 
del p.name 

print(p._name) 
# AttributeError: 'Person' object has no attribute '_name' 


Attributes of Classes :

  • Attributes are the data members of a class that store information.
  • Attributes can be public or private and can be accessed from inside or outside of the class.

The Getter and Setter methods :

  • The getter and setter approach is a way to manage attributes in Python that involves creating methods to read and write attribute values.
  • Code Sample : 


class MyClass:
   def __init__(self):
       self._x = None
   
   def get_x(self):
       return self._x
   
   def set_x(self, value):
       self._x = value
       
       
       

Managing class attributes in Python :

  • Python provides a more elegant approach to managing attributes using the  property()  built-in function.
  • The  property()  function is used to add managed attributes to a class.
  • Different uses of   property()  function in python : 
    • Creating getter and setter methods of a class
    • Using  property()  as a Decorator
    • Creating Read-Only Attributes
    • Creating write-only Attributes
    • Creating Read-Write Attributes
    • Creating computed Attributes
    • Caching computed Attributes
    • Managing Attribute Deletion
    • Validating Input Values
    • Logging Attribute behavior
    • Overriding parent class behavior
    • Creating Backward-Compatible Class APIs

Creating getter and setter methods of a class() :

  • We can use the  property()  function to create managed attributes that allow us to get, set, or delete an attribute's value.
  • Code Sample :

class MyClass:
   def __init__(self):
       self._x = None
   
   def get_x(self):
       return self._x
   
   def set_x(self, value):
       self._x = value
       
   def del_x(self):
       del self._x
       
   x = property(get_x, set_x, del_x)
   
   
   

Using property() as a Decorator

  • We can use the  property()  function as a decorator to define managed attributes.
  • Code Sample :

class MyClass:
   def __init__(self):
       self._x = None
   
   @property
   def x(self):
       return self._x
   
   @x.setter
   def x(self, value):
       self._x = value
       
   @x.deleter
   def x(self):
       del self._x
       
       
       

Creating Read-Only Attributes using properties :

  • We can use properties to create read-only attributes.
  • Code Sample :

class MyClass:
   def __init__(self):
       self._x = None
   
   @property
   def x(self):
       return self._x
       
       
       

Creating write-only Attributes using properties :

  • We can use properties to create write-only attributes.
  • Code Sample :

class MyClass:
   def __init__(self):
       self._x = None
   
   @property
   def x(self):
       raise AttributeError("Attribute 'x' is write-only")
   
   @x.setter
   def x(self, value):
       self._x = value
       
       
       

Creating Read-Write Attributes using properties :

  • We can use properties to create attributes that can be both read and written.
  • Code Sample :

class MyClass:
   def __init__(self):
       self._x = None
   
   @property
   def x(self):
       return self._x
   
   @x.setter
   def x(self, value):
       self._x = value
       
       
       

Creating computed Attributes :

  • We can use properties to provide computed attributes.
  • Code Sample :


import math
class Circle:
   def __init__(self, radius):
       self._radius = radius
   
   @property
   def radius(self):
       return self._radius
   
   @radius.setter
   def radius(self, value):
       if value < 0:
           raise ValueError("Value of 'radius' must be non-negative")
       self._radius = value
       
   @property
   def area(self):
       return math.pi * self._radius  2
       
       
       

Caching computed Attributes :

  • We can use properties to cache computed attributes.
  • Code Sample :

import math

class Circle:
   def __init__(self, radius):
       self._radius = radius
       self._area = None
   
   @property
   def radius(self):
       return self._radius
   
   @radius.setter
   def radius(self, value):
       if value < 0:
           raise ValueError("Value of 'radius' must be non-negative")
       self._radius = value
       self._area = None
       
   @property
   def area(self):
       if self._area is None:
           self._area = math.pi * self._radius  2
       return self._area
       
       
       
       

Managing Attribute Deletion :

  • We can use properties to manage attribute deletion.
  • Code Sample :

class MyClass:
   def __init__(self):
       self._x = None
   
   @property
   def x(self):
       return self._x
   
   @x.deleter
   def x(self):
       raise AttributeError("Cannot delete attribute 'x'")
       
       
       
       

Validating Input Values :

  • We can use properties to validate input values before setting attribute values.
  • Code Sample :

class MyClass:
   def __init__(self):
       self._x = None
   
   @property
   def x(self):
       return self._x
   
   @x.setter
   def x(self, value):
       if value < 0:
           raise ValueError("Value of 'x' must be non-negative")
       self._x = value
       
       
       

Logging Attribute behavior :

  • We can use properties to log attribute access and mutation.
  • Code Sample :


import logging
class MyClass:
   def __init__(self, value):
       self._x = value
   
   @property
   def x(self):
       logging.info("Getting value of 'x'")
       return self._x
   
   @x.setter
   def x(self, value):
       logging.info("Setting value of 'x' to %s", value)
       self._x = value
       
       
       
 

Creating Backward-Compatible Class APIs :

  • We can use properties to create backward-compatible class APIs.
  • Code Sample :

class MyOldClass:
   def __init__(self, value):
       self.value = value
   
   def getValue(self):
       return self.value
   
   def setValue(self, value):
       self.value = value

class MyClass:
   def __init__(self, value):
       self._value = value
   
   @property
   def value(self):
       return self._value
   
   @value.setter
   def value(self, value):
       self._value = value
   
   def getValue(self):
       return self.value
   
   def setValue(self, value):
       self.value = value
       
       
       

Overriding parent class behavior :

  • We can use properties in subclasses to override behavior defined in parent classes.

Prev. Tutorial : Class Decorators

Next Tutorial : Named Tuples