Properties in Python (53/100 Days of Python)
In Python, properties are a type of attribute that allow for controlled access to an object’s internal data. They are a way of defining getter and setter methods for an attribute, which can be useful in many cases where you want to maintain control over how your data is accessed or manipulated.
Defining Properties in Python
To define a property in Python, you use the built-in property
function. The property
function takes up to three arguments: the getter function, the setter function, and the deleter function (which is optional). The getter function is called when you try to access the property, the setter function is called when you try to set the property, and the deleter function is called when you try to delete the property. Here's a basic example of how to define a property:
class MyClass:
def __init__(self):
self._my_property = None
@property
def my_property(self):
return self._my_property
@my_property.setter
def my_property(self, value):
print('Custom update logic goes here...')
self._my_property = value
In this example, we define a class called MyClass
and a property called my_property
. The property has a getter function that simply returns the value of _my_property
, and a setter function that sets the value of _my_property
to whatever value is passed in.
Note that we’ve used a naming convention here where the name of the property is the same as the name of the getter function. This is not required, but it’s a common convention that makes your code more readable.
Accessing Properties in Python
Once you’ve defined a property, you can access it like any other attribute:
obj = MyClass()
obj.my_property = 42
print(obj.my_property)
In this example, we create an instance of MyClass
, set the value of my_property
to 42, and then print out the value of my_property
. When we access the my_property
attribute, the my_property
getter function is called, which returns the value of _my_property
.
Using Properties to Control Access to Data
Properties can be useful for controlling access to data in your objects. For example, you might want to ensure that a certain attribute is always within a certain range, or that it can only be set by certain users. Here’s an example of how to use a property to control access to an attribute:
class MyClass:
def __init__(self):
self._age = None
@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
In this example, we define a class called MyClass
with a property called age
. The age
property has a getter function that returns the value of _age
, and a setter function that first checks if the new value is negative. If the new value is negative, it raises a ValueError
exception. Otherwise, it sets the value of _age
to the new value.
This is a simple example, but it shows how you can use properties to enforce constraints on your data. You could use a similar approach to ensure that an attribute is always within a certain range, or that it can only be set by certain users.
Using Properties to Hide Implementation Details
Another use of properties is to hide implementation details from users of your code. For instance, you might have an attribute that is used internally by your object, but which should not be directly accessed or modified by users of your code. Here’s an example of how to use a property to hide implementation details:
class MyClass:
def __init__(self):
self._data = [1, 2, 3]
@property
def data(self):
return self._data
my_class = MyClass() # MyClass object
print(my_class.data) # [1, 2, 3]
my_class.data = [4, 5, 6] # AttributeError: can't set attribute
print(my_class.data) # [1, 2, 3]
In this example, we define a class called MyClass
with a property called data
. The data
property has a getter function that returns the value of _data
, and a setter function that raises an AttributeError
exception if you try to set the value of data
.
By using a property in this way, we’re hiding the implementation details of our object from users of our code. They can still access and manipulate the data
attribute, but they have to do it through the data
property, which provides a layer of abstraction.
Using Properties with Computed Values
One of the most popular use cases of properties is to compute a value on-the-fly, rather than storing it as a separate attribute. For example, you might have an object with a width
and height
attribute, and you want to compute the area
of the object on-the-fly, rather than storing it as a separate attribute. Here's an example:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
In this example, we define a class called Rectangle
with width
and height
attributes. We also define a property called area
that computes the area of the rectangle by multiplying the width and height together.
By using a property in this way, we can avoid storing the area
as a separate attribute, which can save memory and simplify our code.
What’s next?
- If you found this story valuable, please consider clapping multiple times (this really helps a lot!)
- Hands-on Practice: Free Python Course
- Full series: 100 Days of Python
- Previous topic: Data Classes in Python
- Next topic: Static Methods