If you want to make a single attribute read-only on a class, the easiest way to do it is to make a
property representing your attribute.
Let's say we have a
Thing class with
class Thing: def __init__(self, value, color): self.value = value self.color = color
We want the
color to be changeable after the class has been made:
>>> thing = Thing(4, 'red') >>> thing.color 'red' >>> thing.color = 'blue' >>> thing.color 'blue'
But we don't want the
value to be changeable (it is right now!).
We could try to override the
__setattr__ method, which is called whenever any attribute is assigned and then do something special when
value is assigned... but that's more complicated than we need for this scenario.
The simplest way to make one attribute immutable is to make a property for that attribute:
class Thing: def __init__(self, value, color): self._value = value self.color = color @property def value(self): return self._value
property decorator makes it so that whenever we attempt to access the
value attribute, that
value method we defined will be called and its return value will be given to us:
>>> thing = Thing(4, 'red') >>> thing.value 4
We've made a getter for this property but not a setter (see the links at the bottom of this page for more on properties) so we won't be able to set this property:
>>> thing = Thing(4, 'red') >>> thing.value = 5 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: can't set attribute
That's exactly the behavior we're looking for! 🎉
Note that we're not actually storing a
value attribute on our
Thing class above... we're storing a
_value attribute instead.
The reason is that if instead of saying
self._value = value, we said
self.value = value in our initializer, the setter for our property would be called, which doesn't exist.
Accessing or assigning to
self.value will always go through our property getter/setter now.
We need to actually store our data somewhere and we've chosen to put an
_ before the attribute we're storing to note that it's an internal implementation detail of our class which shouldn't be touched from the outside.
It still can be changed from the outside (no one's stopping us from doing that) but if you ever see code that reaches into another class and reads or writes to
_-prefixed attributes, you should squint at it because something weird is going on. 🤔
>>> thing = Thing(4, 'red') >>> thing._value = 5 >>> thing.value 5
Hello friendly web visitor! 👋
This page is part of Python Morsels, an online Python skill-building service.
The best way to learn is by doing. In the case of Python that means writing Python code. If you'd like to improve your Python skills every week, try out Python Morsels by entering your email below to create an account.
Python Morsels topics pages are free and the first month's worth of exercises is free as well. You don't need to enter payment details to sign up.
You can find explanations of many other Python topics by signing up below.