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 value
and color
attributes:
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
That 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
property
decoratorHello 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.
By signing up, you agree to the Privacy Policy.