How should I expose read-only fields from Python classes?

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:

  1. Use a getter (ugh!).

    class Article:
        def __init__(self, name, available):
            self._name = name
            self.available = available
    
        def name(self):
            return self._name
    

    Ugly, 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.

  2. 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] = value
    

    Looks 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?

  3. 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 = available
    

    The 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 = name to ro_property(self, “name”, name).

This looks quite neat to me – can anybody see a downside with it?

How should I expose database classes over web services?

Given that directly exposing linq to sql or entity framework classes over web services is a bad idea, how should I expose the data over a web service such as WCF? It seems like I would have to create

How can I hide Create button from tree view in a specific object. As this object has all the fields readonly

How can I hide Create button from tree view in a specific object? As this object has all the fields readonly.

Readonly fields in django formset

I’m using modelformset factory to generate formset from model fields. Here i want to make only the queryset objects as readonly and other (extra forms) as non readonly fields How can i achieve this?

readonly-fields as targets from subclass constructors

A readonly field should be used when you have a variable that will be known at object-instatiation which should not be changed afterwards. However one is not allowed to assign readonly fields from con

Can readonly fields be public?

I’ve got a class with readonly fields that indicate the state of my object. These fields should be visible from the outside. At first I showed these fields with properties like this: public class MyCl

How should I expose a collection on a UserControl?

I’m creating a UserControl which will be used in various scenarios. I need to expose a collection of strings from the UserControl and I’m not sure how to do it. The two possible uses I see are: a co

Should I expose the properties of my classes?

During all my studies I learned that when ever I want to give a user access to an inner property, I shouldn’t give a direct access to the property, but give access through get/set method, for example:

How should I expose hierarchical data from a TreeView control to a MVP presenter?

I have some hierarchical data in a Winforms TreeView control and I need to expose it as a property so my presenter can synchronize changes to it. Just to be clear, I’m using the Passive View pattern.

How should I expose an enum?

Here’s my situation. I have a library that has a set of enums that I need to use. public enum showtype { comedy = 1001, horror = 1002, mystery = 1003, action = 1004; } This library is being referenc

How to expose behavior from directive with isolated scope?

How can I expose a method from directive? I know that I should use attributes for data, but I really want to expose behavior, not data. Something that the parent controller can call. Let’s say my DOM

Answers

I would stick with your option 1 but refined it to use Python property:

class Article
    def get_name(self):
        return self.__name

    name = property(get_name)

I would use property as a decorator to manage your getter for name (see the example for the class Parrot in the documentation). Use, for example, something like:

class Article(object):
    def __init__(self, name, available):
        self._name = name
        self.available = available

    @property
    def name(self):
        return self._name

If you do not define the setter for the name property (using the decorator x.setter around a function) this throws an AttributeError when you try and reset name.


Note: You have to use Python’s new-style classes (i.e. in Python 2.6 you have to inherit from
object) for properties to work correctly.
This is not the case according to @SvenMarnach.

It should be noted that it’s always possible to modify attributes of an object in Python – there are no truly private variables in Python. It’s just that some approaches make it a bit harder. But a determined coder can always lookup and modify the value of an attribute. For example, I can always modify your __setattr__ if I want to…

For more information, see Section 9.6 of The Python Tutorial. Python uses name mangling when attributes are prefixed with __ so the actual name at runtime is different but you could still derive what that name at runtime is (and thus modify the attribute).

Based in the Chris answer, but arguably more pythonic:

def ro_property(field):
    return property(lambda self : self.__dict__[field])

class Article(object):
    name = ro_property('_name')

    def __init__(self):
        self._name = "banana"

If trying to modify the property it will raise an AttributeError.

a = Article()
print a.name # -> 'banana'
a.name = 'apple' # -> AttributeError: can't set attribute

UPDATE: About your updated answer, the (little) problem I see is that you are modifying the definition of the property in the class every time you create an instance. And I don’t think that is such a good idea. That’s why I put the ro_property call outside of the __init__ function

What about?:

def ro_property(name):
    def ro_property_decorator(c):
        setattr(c, name, property(lambda o: o.__dict__["_" + name]))
        return c
    return ro_property_decorator

@ro_property('name')
@ro_property('other')
class Article(object):
    def __init__(self, name):
        self._name = name
        self._other = "foo"

a = Article("banana")
print a.name # -> 'banana'
a.name = 'apple' # -> AttributeError: can't set attribute

Class decorators are fancy!

As pointed out in other answers, using a property is the way to go for read-only attributes. The solution in Chris’ answer is the cleanest one: It uses the property() built-in in a straight-forward, simple way. Everyone familiar with Python will recognize this pattern, and there’s no domain-specific voodoo happening.

If you don’t like that every property needs three lines to define, here’s another straight-forward way:

from operator import attrgetter

class Article(object):
    def __init__(self, name, available):
        self._name = name
        self.available = available
    name = property(attrgetter("_name"))

Generally, I don’t like defining domain-specific functions to do something that can be done easily enough with standard tools. Reading code is so much easier if you don’t have to get used to all the project-specific stuff first.