How to Use Python Properties to Write Cleaner, More Efficient Code

Have you ever thought about customizing how you access or update an attribute? Properties let you do just that. In Python programming, properties offer a smart way to manage and control your class attributes. They are also utilized for adding extra logic behind simple attribute operations.

In today’s guide, we will discuss Python properties and show you how they can make your code cleaner and more organized.

What are Properties in Python Class

Properties in Python refer to the bridge between the methods and the attributes. They enable you to have controlled access and apply encapsulation as well.

Additionally, properties make sure that the data integrity is maintained while enhancing flexibility and dynamism in class design.

Python properties improve the readability and maintainability of the code by offering a standardized interface for attribute manipulation.

What are Property Decorators in Python

In Python, property decorators are denoted by the “@” symbol. These decorators are used for transforming methods into properties, which significantly simplify the access of the attribute.

The “@property“. “@.setter“, and “@.deleter” decorators define the behavior of the getter, setter, and deleter method, accordingly.

With the help of decorators, properties can be easily integrated into class structures.

How to Create and Use Property Function in Python

This section will demonstrate different approaches for creating and using properties, such as implementing getter methods, setting property values using setter methods, and deleting properties using deleter methods.

1. Implement Getter Methods

Getter methods are defined using the “@property” decorator. This permits control over the retrieval of the attribute values.

Getter methods also enable you to customize the way attributes can be accessed. Moreover, they make sure that the attribute values are fetched with consistency and precision.

For instance, in the below program, the “radius” attribute is accessed using the gettering method “@property” and then its value has been printed on the console.

class Circle:
    def __init__(self, radius):
        self._radius = radius
   
    @property
    def radius(self):
        return self._radius
   
circle = Circle(5)
print(circle.radius)
Implement Getter Methods in Python
Implement Getter Methods in Python

2. Set Property Values with Setter Methods

The “@.setter” syntax can be utilized for defining the setter method. Unlike getter methods, they provide control over the assignment of the attribute values.

Additionally, these methods enable you to verify the incoming values and enforce constraints before updating attributes.

The setter method makes sure that the attribute assignment follows the defined rules as well.

Here, in the below code, the “width” attribute value has been updated with the setter method named “@width.setter“.

class Rectangle:
    def __init__(self, width, height):
        self._width = width
        self._height = height
   
    @property
    def width(self):
        return self._width
   
    @width.setter
    def width(self, value):
        if value > 0:
            self._width = value
        else:
            raise ValueError("Width must be positive.")
   
rectangle = Rectangle(4, 3)
rectangle.width = 5
print(rectangle.width)
Setter Methods in Python
Setter Methods in Python

3. Delete Properties with Deleter Methods

To create a deleter method in Python, utilize the “@.deleter” syntax. These types of methods allow you to control the deletion or removal of the attribute values.

You can use the deleter method for performing the cleanup operations or resource management operations when an attribute gets deleted. Overall, these methods play an essential role in maintaining a consistent codebase.

Here, we will now delete the “side” attribute by invoking the “@side.deleter” method. This method provides the opportunity for performing required cleanup operations.

class Square:
    def __init__(self, side):
        self._side = side
   
    @property
    def side(self):
        return self._side
   
    @side.deleter
    def side(self):
        print("Deleting the side attribute")
        del self._side
   
square = Square(6)
del square.side
Deleter Methods in Python
Deleter Methods in Python

Advanced Property Techniques in Python

This section will explore the advanced property techniques that can improve attribute manipulation which consequently optimizes the performance. For instance, computed properties introduce dynamic attribute values based on evaluations or calculations.

On the other hand, property caching maximized efficiency by storing previously computed results for reuse.

1. Computed Properties and Lazy Evaluation

Computed properties can be defined using methods that are invoked when the property is accessed. This results in real-time computations. Moreover, computed properties enable efficient memory usage and offer up-to-date values, which improves class design flexibility.

In the provided program, the “diameter” property will be computed according to the value of the “radius” attribute value. This property dynamically calculates the diameter value whenever it is accessed.

class Circle:
    def __init__(self, radius):
        self._radius = radius
   
    @property
    def diameter(self):
        return self._radius * 2
   
circle = Circle(5)
print(circle.diameter)
Computed Properties and Lazy Evaluation in Python
Computed Properties and Lazy Evaluation in Python

2. Property Caching for Performance

Property caching techniques can be implemented for optimizing performance. This is done by storing the recently computed property values, which also reduces the redundant calculations.

This approach is particularly helpful when property calculations are time-consuming or resource-intensive.

Here, the “FactorialCalculatorclass computes the factorial of the specified number with the “_calculate_factorial” method.

Note that the “factorial” property caches the calculated factorial result with the help of the “_factorial_cachedictionary.

When the property will be accessed multiple times, the cached result has been returned, which ultimately avoids redundant calculations.

class FactorialCalculator:
    def __init__(self):
        self._factorial_cache = {}

    @property
    def factorial(self):
        if 'result' in self._factorial_cache:
            return self._factorial_cache['result']
       
        print("Calculating factorial...")
        result = self._calculate_factorial()
        self._factorial_cache['result'] = result
        return result
   
    def _calculate_factorial(self):
        # Simulate a time-consuming factorial calculation
        import time
        time.sleep(2)
        n = 5
        factorial = 1
        for i in range(1, n + 1):
            factorial *= i
        return factorial

# Create an instance of the class
calculator = FactorialCalculator()

# Access the factorial property
print("Factorial:", calculator.factorial)

# Access the factorial property again (cached)
print("Factorial:", calculator.factorial)
Property Caching for Performance in Python
Property Caching for Performance in Python

Encapsulation and Data Hiding in Python

Encapsulation and data hiding are the core principles of object-oriented programming that contribute to the organization, maintenance, and overall security of the code.

More specifically, Python properties offer a solution for achieving encapsulation by enabling controlled access to the class attributes.

According to the given program, the “BankAccount” class encapsulates the “balance” attribute using properties. The “balance” property makes sure that the attribute value remains non-negative.

class BankAccount:
    def __init__(self, balance):
        self._balance = balance
   
    @property
    def balance(self):
        return self._balance
   
    @balance.setter
    def balance(self, amount):
        if amount >= 0:
            self._balance = amount
        else:
            raise ValueError("Balance cannot be negative.")
   
    def deposit(self, amount):
        self.balance += amount
   
    def withdraw(self, amount):
        if self.balance >= amount:
            self.balance -= amount
        else:
            raise ValueError("Insufficient funds.")
   
# Create a bank account instance
account = BankAccount(1000)

# Access and modify balance using properties
print("Initial Balance:", account.balance)
account.deposit(500)
print("Updated Balance:", account.balance)
account.withdraw(200)
print("Remaining Balance:", account.balance)
Encapsulation and Data Hiding in Python
Encapsulation and Data Hiding in Python

Property-Based Configuration and Customization in Python

Python properties also offer a versatile mechanism for configuring and customizing the object behavior. This enables you to create dynamic and adaptable classes.

By implementing the properties for controlling specific attributes or behaviors, you can customize objects to different use cases without changing the underlying class structure.

In the following example, the “Car” class utilized properties to configure the “color” attribute. So, by exposing the property for color customization, you can easily adjust the car’s appearance without altering the core attributes of the class.

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self._color = "Black"
   
    @property
    def color(self):
        return self._color
   
    @color.setter
    def color(self, new_color):
        self._color = new_color
   
    def display_info(self):
        return f"{self.year} {self.make} {self.model} ({self.color})"
   
# Create a car instance
my_car = Car("Toyota", "Camry", 2022)

# Customize car color using properties
print("Original:", my_car.display_info())
my_car.color = "Blue"
print("Customized:", my_car.display_info())
Property-Based Configuration and Customization in Python
Property-Based Configuration and Customization in Python

Naming Convention for Properties in Python

Here are some of the best practices for naming properties in Python:

  • Descriptive Names: Choose clear and meaningful names that reflect the property’s purpose.
  • CamelCase: Use CamelCase for consistent property naming, aligned with Python standards.
  • Public Properties: Avoid single underscores for public properties; use descriptive names.
  • Protected Properties: Prefix with a single underscore for protected properties.
  • Private Properties: Prefix with double underscores for private properties.
  • Distinguish Properties: Ensure a clear distinction between properties and methods.
  • Plural Form: Use plural form for properties representing collections.
  • Follow Python Guidelines: Follow Python’s naming conventions for uniformity.

That’s all from this informative guide related to Python property.

Conclusion

Python properties offer a toolkit for building better classes. Through our today’s guide, you are now all ready to use the attributes with customized getters, setters, and deleters.

We have also discussed how properties can be utilized for optimization, lazily calculating stuff only when needed. With properties in your toolbox, you are all ready for writing cleaner, more efficient, and more maintainable code.

Want to explore and learn more related to Python, do check out our dedicated Python Tutorial Series!

If you read this far, tweet to the author to show them you care. Tweet a thanks
As a professional content writer with 3 years of experience, I specialize in creating high-quality, SEO-optimized content that engages, attracts, and retains the audience.

Each tutorial at GeeksVeda is created by a team of experienced writers so that it meets our high-quality standards.

Join the GeeksVeda Weekly Newsletter (More Than 5,467 Programmers Have Subscribed)
Was this article helpful? Please add a comment to show your appreciation and support.

Got Something to Say? Join the Discussion...