With Python’s property(), you can create managed attributes in your classes. You can use managed attributes, also known as properties, when you need to modify their internal implementation without changing the public API of the class. Providing stable APIs can help you avoid breaking your users’ code when they rely on your classes and objects.
Properties are arguably the most popular way to create managed attributes quickly and in the purest Pythonic style.
In this tutorial, you’ll learn how to:
Create managed attributes or properties in your classes
Perform lazy attribute evaluation and provide computed attributes
Avoid setter and getter methods to make your classes more Pythonic
Create read-only, read-write, and write-only properties
Create consistent and backward-compatible APIs for your classes
You’ll also write a few practical examples that use property() for validating input data, computing attribute values dynamically, logging your code, and more. To get the most out of this tutorial, you should know the basics of object-oriented programming and decorators in Python.
Free Bonus: 5 Thoughts On Python Mastery, a free course for Python developers that shows you the roadmap and the mindset you’ll need to take your Python skills to the next level.
When you define a class in an object-oriented programming language, you’ll probably end up with some instance and class attributes. In other words, you’ll end up with variables that are accessible through the instance, class, or even both, depending on the language. Attributes represent or hold the internal state of a given object, which you’ll often need to access and mutate.
Typically, you have at least two ways to manage an attribute. Either you can access and mutate the attribute directly or you can use methods. Methods are functions attached to a given class. They provide the behaviors and actions that an object can perform with its internal data and attributes.
If you expose your attributes to the user, then they become part of the public API of your classes. Your user will access and mutate them directly in their code. The problem comes when you need to change the internal implementation of a given attribute.
Say you’re working on a Circle class. The initial implementation has a single attribute called .radius. You finish coding the class and make it available to your end users. They start using Circle in their code to create a lot of awesome projects and applications. Good job!
Now suppose that you have an important user that comes to you with a new requirement. They don’t want Circle to store the radius any longer. They need a public .diameter attribute.
At this point, removing .radius to start using .diameter could break the code of some of your end users. You need to manage this situation in a way other than removing .radius.
Programming languages such as Java and C++ encourage you to never expose your attributes to avoid this kind of problem. Instead, you should provide getter and setter methods, also known as accessors and mutators, respectively. These methods offer a way to change the internal implementation of your attributes without changing your public API.
Note: Getter and setter methods are often considered an anti-pattern and a signal of poor object-oriented design. The main argument behind this proposition is that these methods break encapsulation. They allow you to access and mutate the components of your objects.
In the end, these languages need getter and setter methods because they don’t provide a suitable way to change the internal implementation of an attribute if a given requirement changes. Changing the internal implementation would require an API modification, which can break your end users’ code.
Technically, there’s nothing that stops you from using getter and setter methods in Python. Here’s how this approach would look:
def __init__(self, x, y):
self._x = x
self._y = y
def set_x(self, value):
self._x = value
def set_y(self, value):
self._y = value
In this example, you create Point with two non-public attributes ._x and ._y to hold the Cartesian coordinates of the point at hand.
Note: Python doesn’t have the notion of access modifiers, such as private, protected, and public, to restrict access to attributes and methods. In Python, the distinction is between public and non-public class members.
If you want to signal that a given attribute or method is non-public, then you have to use the well-known Python convention of prefixing the name with an underscore (_). That’s the reason behind the naming of the attributes ._x and ._y.
Note that this is just a convention. It doesn’t stop you and other programmers from accessing the attributes using dot notation, as in obj._attr. However, it’s bad practice to violate this convention.
To access and mutate the value of either ._x or ._y, you can use the corresponding getter and setter methods. Go ahead and save the above definition of Point in a Python module and import the class into your interactive shell.
Here’s how you can work with Point in your code:
>>> point = Point(12, 5)
>>> # Non-public attributes are still accessible
With .get_x() and .get_y(), you can access the current values of ._x and ._y. You can use the setter method to store a new value in the corresponding managed attribute. From this code, you can confirm that Python doesn’t restrict access to non-public attributes. Whether or not you do so is up to you.
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]