Properties in Python
Introduction to Properties in Python
In Python, properties are attributes of a class that are accessed via methods but behave like regular attributes. Properties are usually defined using the @property, @setter, and @deleter decorators.
Adding Properties to a Child Class
Define a Property in a Parent Class
First, let’s define a parent class with properties.
Example:
class Animal: def __init__(self, name, age): self._name = name self._age = age @property def name(self): return self._name @name.setter def name(self, value): self._name = value @property def age(self): return self._age @age.setter def age(self, value): if value < 0: raise ValueError("Age cannot be negative.") self._age = value
- The properties name and age are defined with getter and setter methods. The @property decorator is used to define a readable attribute, while @name.setter and @age.setter allow you to set values.
Add Properties in the Child Class
Next, we can add specific properties to a child class that inherits from the Animal class.
Example
class Dog(Animal): def __init__(self, name, age, breed): super().__init__(name, age) self._breed = breed @property def breed(self): return self._breed @breed.setter def breed(self, value): self._breed = value
- Dog inherits from Animal and adds a new property breed with its own getter and setter methods.
Using Properties in the Child Class
You can now create instances of the child class and use the properties as if they were regular attributes.
Example:
my_dog = Dog("Rex", 5, "Labrador") print(my_dog.name) # Rex print(my_dog.age) # 5 print(my_dog.breed) # Labrador my_dog.name = "Max" my_dog.age = 6 my_dog.breed = "Golden Retriever" print(my_dog.name) # Max print(my_dog.age) # 6 print(my_dog.breed) # Golden Retriever try: my_dog.age = -1 except ValueError as e: print(e) # Age cannot be negative.
- The properties defined in both the parent and child classes are used to access and modify the object’s attributes.
Properties with Dynamic Calculations
You can also define properties that perform dynamic calculations or depend on other attributes.
Example:
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("Radius cannot be negative.") self._radius = value @property def area(self): import math return math.pi * (self._radius ** 2)
- The area property is computed dynamically based on the radius. It does not have a setter method because the area is directly related to the radius.
Usage:
my_circle = Circle(5) print(my_circle.radius) # 5 print(my_circle.area) # 78.53981633974483 my_circle.radius = 10 print(my_circle.area) # 314.1592653589793
Redefining Properties in the Child Class
You can also redefine properties in a child class to adapt them to specific needs while retaining some inherited functionality.
Example:
class Animal: def __init__(self, name, age): self._name = name self._age = age @property def name(self): return self._name @name.setter def name(self, value): self._name = value @property def age(self): return self._age @age.setter def age(self, value): if value < 0: raise ValueError("Age cannot be negative.") self._age = value class Dog(Animal): def __init__(self, name, age, breed): super().__init__(name, age) self._breed = breed @property def breed(self): return self._breed @breed.setter def breed(self, value): self._breed = value @property def age(self): return super().age @age.setter def age(self, value): if value > 20: raise ValueError("A dog's age cannot exceed 20 years.") super(Dog, type(self)).age.fset(self, value)
- In this example, the age property is redefined in the Dog class to add specific validation for dogs, while still using the parent class’s age property functionality.
Using Properties in Real-World Contexts
Properties are often used in real-world scenarios to encapsulate specific behaviors related to data.
Example:
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: raise ValueError("Width must be positive.") self._width = value @property def height(self): return self._height @height.setter def height(self, value): if value <= 0: raise ValueError("Height must be positive.") self._height = value @property def area(self): return self._width * self._height @property def perimeter(self): return 2 * (self._width + self._height)
Usage:
my_rectangle = Rectangle(4, 5) print(my_rectangle.area) # 20 print(my_rectangle.perimeter) # 18 my_rectangle.width = 6 print(my_rectangle.area) # 30
Summary
- Defining Properties: Use @property, @setter, and @deleter decorators to create properties with getter, setter, and deleter methods.
- Adding Properties in a Child Class: You can add new properties or redefine inherited ones to meet the specific needs of the subclass.
- Dynamic Properties: Properties can perform dynamic calculations based on other attributes.
Validation and Encapsulation: Properties help validate data and encapsulate logic related to accessing and modifying attributes.