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:
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.
Validation: The setter allows you to validate or modify the value before assigning it to the attribute, ensuring that only valid data is set.
Read-Only Properties: You can create read-only properties (computed from other attributes) that can only be accessed and not modified.
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! 😊
0 Comments