I have many different small classes which have a few fields each, e.g. this:
class Article:
def __init__(self, name, available):
self.name = name
self.available = available
What’s the easiest and/or most idiomatic way to make the name field read only, so that
a = Article("Pineapple", True)
a.name = "Banana" # <-- should not be possible
is not possible anymore?
Here’s what I considered so far:
-
Use a getter (ugh!).
class Article: def __init__(self, name, available): self._name = name self.available = available def name(self): return self._nameUgly, non-pythonic – and a lot of boilerplate code to write (especially if I have multiple fields to make read-only). However, it does the job and it’s easy to see why that is.
-
Use
__setattr__:class Article: def __init__(self, name, available): self.name = name self.available = available def __setattr__(self, name, value): if name == "name": raise Exception("%s property is read-only" % name) self.__dict__[name] = valueLooks pretty on the caller side, seems to be the idiomatic way to do the job – but unfortunately I have many classes with only a few fields to make read only each. So I’d need to add a
__setattr__implementation to all of them. Or use some sort of mixin maybe? In any case, I’d need to make up my mind how to behave in case a client attempts to assign a value to a read-only field. Yield some exception, I guess – but which? -
Use a utility function to define properties (and optionally getters) automatically. This is basically the same idea as (1) except that I don’t write the getters explicitely but rather do something like
class Article: def __init__(self, name, available): # This function would somehow give a '_name' field to self # and a 'name()' getter to the 'Article' class object (if # necessary); the getter simply returns self._name defineField(self, "name") self.available = availableThe downside of this is that I don’t even know if this is possible (or how to implement it) since I’m not familiar with runtime code generation in Python. 🙂
So far, (2) appears to be most promising to me except for the fact that I’ll need __setattr__ definitions to all my classes. I wish there was a way to ‘annotate’ fields so that this happens automatically. Does anybody have a better idea?
For what it’s worth, I’mu sing Python 2.6.
UPDATE:
Thanks for all the interesting responses! By now, I have this:
def ro_property(o, name, value):
setattr(o.__class__, name, property(lambda o: o.__dict__["_" + name]))
setattr(o, "_" + name, value)
class Article(object):
def __init__(self, name, available):
ro_property(self, "name", name)
self.available = available
This seems to work quite nicely. The only changes needed to the original class are
- I need to inherit
object(which is not such a stupid thing anyway, I guess) - I need to change
self._name = nametoro_property(self, "name", name).
This looks quite neat to me – can anybody see a downside with it?
I would use
propertyas a decorator to manage your getter forname(see the example for the classParrotin the documentation). Use, for example, something like:If you do not define the setter for the
nameproperty (using the decoratorx.setteraround a function) this throws anAttributeErrorwhen you try and resetname.Note: You have to use Python’s new-style classes (i.e. in Python 2.6 you have to inherit fromThis is not the case according to @SvenMarnach.object) for properties to work correctly.