Use of Python @property
Using Python @property

1. Introduction
In Python, we often need to define classes to represent objects. These objects typically have attributes that need to be manipulated and accessed. In Python, we can use the @property decorator to encapsulate class attributes, providing more flexible and secure access.
This article will detail the usage and common application scenarios of @property, and use sample code to demonstrate the practical value of @property in real-world development.
2. Basic Usage of @property
@property is a decorator function that can be used to define a property getter method. Here’s a basic example of @property usage:
class MyClass:
def __init__(self):
self._my_property = None
@property
def my_property(self):
return self._my_property
Code Analysis:
– A property named my_property is defined in the MyClass class.
– The @property decorator converts the my_property method into a read-only attribute. This means that the property’s value can only be retrieved by calling the my_property method; the property cannot be assigned directly.
Below is an example code using the @property decorator:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@property
def area(self):
return 3.14 * self._radius**2
circle = Circle(5)
print(circle.radius) # Output: 5
print(circle.area) # Output: 78.5
Code analysis:
– A class named Circle is defined, which has a radius attribute and an area attribute.
– The radius method uses the @property decorator to convert it into a read-only attribute. The value of the attribute can be obtained by calling the radius method.
– The area method also uses the @property decorator to convert it to a read-only property.
– Creates a Circle object, circle, and calls its radius and area properties. The output is 5 and 78.5, respectively.
3. Advanced Uses of @property
The @property decorator can also be used with other decorators to implement more complex functionality.
3.1 Setting Property Setters
Properties defined with the @property decorator are read-only by default, meaning they cannot be assigned values. If you want to be able to set the property’s value, you need to define a setter method and bind it to the property using the @property.setter decorator.
Here is a sample code:
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, width):
if width <= 0:
raise ValueError("Width must be greater than 0")
self._width = width
@property
def height(self):
return self._height
@height.setter
def height(self, height):
if height <= 0:
raise ValueError("Height must be greater than 0")
self._height = height
@property
def area(self):
return self._width * self._height
rectangle = Rectangle(5, 10)
print(rectangle.width) # Output: 5
print(rectangle.height) # Output: 10
print(rectangle.area) # Output: 50
rectangle.width = 8
rectangle.height = 12
print(rectangle.width) # Output: 8
print(rectangle.height) # Output: 12
print(rectangle.area) # Output: 96
rectangle.width = -1 # Throws exception: Width must be greater than 0
Code Analysis:
– Creates a class named Rectangle with properties named width, height, and area.
– Defines getter methods for width and height and binds the setter methods to the properties using the @property.setter decorator.
– Creates a Rectangle object named rectangle and gets and sets the values of its width, height, and area properties, respectively.
The width.setter and height.setter decorators in the example code convert the width and height methods, respectively, into writable properties. We can modify their values by assigning them values.
Note that within the setter methods, we can add some logic to control the range of the property values. For example, in the example code, if a value less than or equal to 0 is assigned to width or height, an exception will be thrown.
3.2 Deleter Method for Deleting Properties
In addition to setting and getting property values, class properties sometimes also require a method for deleting them. To do this, we can use the @property.deleter decorator to define a deleter method.
Here is an example code:
class Square:
def __init__(self, length):
self._length = length
@property
def length(self):
return self._length
@length.setter
def length(self, length):
if length <= 0:
raise ValueError("Side length must be greater than 0")
self._length = length
@length.deleter
def length(self):
del self._length
square = Square(5)
print(square.length) # Output: 5
del square.length
print(square.length) # Throws exception: 'Square' object has no attribute '_length'
Code analysis:
– Creates a class called Square with a length attribute.
– Defines a getter method for length and uses the @property.setter decorator to bind the setter method to a property.
– Creates a Square object, square, and retrieves the value of its length property.
– Deletes square’s length property using the del keyword. When trying to retrieve the property’s value again, an exception is thrown.
In the above code example, the length.deleter decorator converts the length method into a deletable property. We can delete the property using the del keyword.
4. Benefits of Using @property
Using the @property decorator provides the following benefits:
4.1 Encapsulating Properties
The @property decorator encapsulates property accessor methods. This allows external accessors to avoid knowing the underlying implementation details, improving code maintainability and readability.
4.2 Adding Property Validation Logic
In the setter method of the @property decorator, we can add some logical checks to ensure the validity of the property. This way, when the property value does not meet the requirements, we can throw an exception or perform other processing, thereby improving the robustness of the code.
4.3 More Flexible and Secure Access Methods
Using the @property decorator, property access is changed from the traditional obj.attr method to obj.attr(). This method is more flexible and allows you to perform other operations while reading or setting the property. It also improves property access security and provides more precise control over property reading and writing.
5. Summary
This article detailed the basic and advanced usage of the @property decorator and demonstrated its practical application scenarios and benefits through code examples. By using the @property decorator, we can encapsulate properties, add property validation logic, and provide more flexible and secure property access, thereby improving code maintainability, readability, and robustness.
In addition to the benefits mentioned above, the @property decorator has the following application scenarios:
5.1 Computed Properties
Sometimes, we need to calculate a new property based on the values of other properties. The @property decorator makes it easy to implement such computed properties.
Here is a sample code:
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, width):
if width <= 0:
raise ValueError("Width must be greater than 0")
self._width = width
@property
def height(self):
return self._height
@height.setter
def height(self, height):
if height <= 0:
raise ValueError("Height must be greater than 0")
self._height = height
@property
def area(self):
return self._width * self._height
@property
def perimeter(self):
return 2 * (self._width + self._height)
rectangle = Rectangle(5, 10)
print(rectangle.area) # Output: 50
print(rectangle.perimeter) # Output: 30
rectangle.width = 8
rectangle.height = 12
print(rectangle.area) # Output: 96
print(rectangle.perimeter) # Output: 40
Code Analysis:
– A class named Rectangle is created with properties named width, height, area, and perimeter.
– The area and perimeter properties are both defined using the @property decorator; they are obtained by calculating the values of the width and height properties.
– A Rectangle object named rectangle is created, and the values of its area and perimeter properties are retrieved.
By using the @property decorator, we can encapsulate complex calculation logic in computed properties, making the code more concise and easier to read.
5.2 Chaining
Sometimes, we want to set multiple properties through chaining. This can be achieved using the @property decorator.
Here is a sample code:
class Person:
def __init__(self):
self._name = ""
self._age = 0
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
def set_name(self, name):
self._name = name
return self
def set_age(self, age):
self._age = age
return self
person = Person()
person.set_name("Alice").set_age(20)
print(person.name) #Output: Alice
print(person.age) # Output: 20
Code Analysis:
– A class named Person is created with name and age properties.
– The set_name and set_age methods are used to set the values of the name and age properties, respectively, and return self, implementing chaining.
– A Person object, person, is created and the name and age properties are set using chaining.
By using the @property decorator and chaining methods, we can set multiple properties more elegantly.
6. Questions
- Can the @property decorator only be used with instance methods? Why?
- When using the @property decorator, we need to add an underscore to the method name, for example,
_my_property. Why is this naming convention used? - Given the following code, use the @property decorator to implement a read-only property.
class Student:
def __init__(self, name, age):
self._name = name
self._age = age
Answers to Questions
- The @property decorator can be used not only on instance methods but also on class methods and static methods. We can use the @property decorator on class methods or static methods to convert them into class attributes.
- In Python, an underscore prefix is used to indicate a private attribute or method. While Python doesn’t have true private attributes or methods, using an underscore prefix can indicate to other developers that the attribute or method is for internal use and shouldn’t be accessed directly.
- Example code using the @property decorator to implement a read-only property:
class Student:
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@property
def age(self):
return self._age
student = Student("Alice", 20)
print(student.name) # Output: Alice
print(student.age) # Output: 20
student.name = "Bob" # Throws exception: can't set attribute
student.age = 22 # Throws exception: can't set attribute
Code Analysis:
– Creates a class named Student with name and age attributes.
– Both the name and age properties are defined using the @property decorator, making them read-only.
– A Student object is created and the values of its name and age properties are retrieved. Attempting to assign values to these properties will throw an exception.