Property Decorator Getter Setter in Python

 


In Python, the @property decorator is used to define a getter for an attribute, while @property.setter is used to define a setter for the same property. The getter allows you to access a property as if it were an attribute, while the setter allows you to modify that property. Together, they give you control over how the attribute is accessed and modified.

Key Concepts:

  • Getter: The method decorated with @property is called when the property is accessed. It allows you to calculate or retrieve the value of an attribute dynamically.

  • Setter: The method decorated with @property.setter is called when you assign a value to the property. It allows you to add logic to validate or modify the value before it is assigned to the underlying attribute.

Syntax of Getter and Setter

  • class ClassName:

  •     def __init__(self, value):

  •         self._attribute = value  # Private attribute


  •     @property

  •     def attribute(self):

  •         # This is the getter method

  •         return self._attribute


  •     @attribute.setter

  •     def attribute(self, value):

  •         # This is the setter method

  •         self._attribute = value


Example: Using Getter and Setter

Let’s create a class Person with a private attribute _age and define a property age to retrieve and set the age value. We will use a getter to access the age and a setter to ensure that the age cannot be set to a negative value.

  • class Person:

  •     def __init__(self, name, age):

  •         self.name = name

  •         self._age = age  # Private attribute for age


  •     @property

  •     def age(self):

  •         # Getter method

  •         return self._age


  •     @age.setter

  •     def age(self, value):

  •         # Setter method with validation

  •         if value < 0:

  •             raise ValueError("Age cannot be negative")

  •         self._age = value


  • # Creating an instance of Person

  • person = Person("John", 30)


  • # Accessing the age using the getter

  • print(f"{person.name} is {person.age} years old.")  # Output: John is 30 years old.


  • # Modifying the age using the setter

  • person.age = 35

  • print(f"{person.name} is now {person.age} years old.")  # Output: John is now 35 years old.


  • # Trying to set an invalid value

  • try:

  •     person.age = -5  # This will raise a ValueError

  • except ValueError as e:

  •     print(e)  # Output: Age cannot be negative


Explanation:

  • The age property has a getter method (@property) that retrieves the value of the private _age attribute.

  • The setter method (@age.setter) is used to modify the _age attribute, with validation to ensure that the age cannot be negative. If a negative value is assigned, a ValueError is raised.

  • When accessing person.age, the getter method is automatically invoked, and when assigning a new value to person.age, the setter method is called.

Example with Read-Only Property (Only Getter)

Sometimes, you might want to define a property that is read-only, i.e., a property that can only be accessed but not modified. In this case, you define only the getter method and omit the setter.

  • class Circle:

  •     def __init__(self, radius):

  •         self._radius = radius


  •     @property

  •     def radius(self):

  •         return self._radius


  •     @property

  •     def area(self):

  •         # Read-only property

  •         return 3.14159 * (self._radius ** 2)


  • # Creating an instance of Circle

  • circle = Circle(5)


  • # Accessing the radius and area properties

  • print(f"Radius: {circle.radius}")  # Output: Radius: 5

  • print(f"Area: {circle.area}")      # Output: Area: 78.53975


  • # Trying to modify the read-only property will raise an AttributeError

  • try:

  •     circle.area = 100  # This will raise an error because area has no setter

  • except AttributeError as e:

  •     print(e)  # Output: can't set attribute


Using Setter and Getter Together

You can use both the getter and setter methods together for attributes that need to be accessed and modified dynamically:

  • class Temperature:

  •     def __init__(self, celsius):

  •         self._celsius = celsius


  •     @property

  •     def celsius(self):

  •         # Getter: Return Celsius value

  •         return self._celsius


  •     @celsius.setter

  •     def celsius(self, value):

  •         # Setter: Convert to Fahrenheit before assigning

  •         if value < -273.15:

  •             raise ValueError("Temperature cannot be below absolute zero")

  •         self._celsius = value


  •     @property

  •     def fahrenheit(self):

  •         # Read-only property: Convert Celsius to Fahrenheit

  •         return (self._celsius * 9/5) + 32


  • # Creating an instance of Temperature

  • temp = Temperature(25)


  • # Accessing Celsius and Fahrenheit values

  • print(f"Temperature: {temp.celsius}°C")      # Output: Temperature: 25°C

  • print(f"Temperature: {temp.fahrenheit}°F")    # Output: Temperature: 77.0°F


  • # Changing the Celsius value using the setter

  • temp.celsius = 30

  • print(f"Updated Temperature: {temp.celsius}°C")  # Output: Updated Temperature: 30°C

  • print(f"Updated Temperature: {temp.fahrenheit}°F")  # Output: Updated Temperature: 86.0°F


  • # Trying to set a value below absolute zero

  • try:

  •     temp.celsius = -300  # This will raise a ValueError

  • except ValueError as e:

  •     print(e)  # Output: Temperature cannot be below absolute zero


Explanation:

  • The Temperature class uses both a getter (@property) and a setter (@celsius.setter) for the celsius property.

  • The setter ensures that the temperature value cannot be set below absolute zero (-273.15°C).

  • The fahrenheit property is a read-only property (computed dynamically from the celsius value) and doesn't have a setter.

Benefits of Using @property with Getter and Setter:

  1. Encapsulation: You can hide the internal details of how an attribute is accessed and modified. This helps protect the integrity of the data and allows you to change the implementation later without affecting how the attribute is used.

  2. Validation: The setter allows you to validate or modify the value before assigning it to the attribute, ensuring that only valid data is set.

  3. Read-Only Properties: You can create read-only properties (computed from other attributes) that can only be accessed and not modified.

  4. Clean Interface: The getter and setter provide a clean and intuitive interface for interacting with the object, making it look like you're dealing with regular attributes instead of methods.

Conclusion:

The @property decorator is a powerful tool in Python that allows you to control how attributes are accessed and modified in a class. Using @property for getters, @property.setter for setters, and optionally @property.deleter for deleters, you can define dynamic and validated properties that provide a clean interface for interacting with class attributes.

If you have any further questions or need more examples, feel free to ask! 😊


Post a Comment

0 Comments