Read-only Python Attributes

10 AM June 3, 2004

Property descriptors are a powerful tool for building easy to understand APIs. I use read-only properties liberally in my APIs, but it took a few months to arrive at an implementation idiom that I was happy with.

In Kangapy,1 each article is associated with a template, and the template may not change after the article object is constructed. It makes sense for the Article class to have a read-only 'template' attribute.

Based on what I had read of property descriptors, this was my first version of the descriptor code:


  def __getTemplate(self):

      return self.__template

  template = property(__getTemplate, None, None, 

          "This article's defining Template") 

While it provides a neat API for getting the article's template, the implementation is long winded and difficult to read. Compare it with the implementation of the equivalent getter method:


  def getTemplate(self):

      """ This article's defining Template

      """

      return self.__template

Casting around for a better way to write property descriptors, I hit on a methodless idiom that I am still using:


  template = property(lambda self: self.__template) 

Simple derived value properties also work well:


  templateName = property(lambda self: self.__template.name) 

This methodless form of a property is simpler to write and read than the original form. On the down side, it doesn't have a doc string,2 and it still isn't as clear as a getter method.


1 Kangapy is my blogging software. It is currently undergoing its third complete rewrite. After this one, I'll release the source, I promise.

2 I found that burying the doc string inside the property function meant that I wasn't using it while I was editing the code. Since I'm not using pydoc, the doc string was useless.

By alang | # | Comments (7)
(Posted to Python)

Comments

At 13:48, 03 Jun 2004 Simon Brunning wrote:

Read only attributes? A bit old-school static-typeist for my liking. A bit bondage-and-discipline[1].

If you don't want to write to an attribute, just don't do it. If you do it by accident, well, your unit tests will pick it up, won't they. ;-)

[1] http://www.catb.org/~esr/jargon/html/B/bondage-and-discipline-language.html

(#)
At 17:20, 03 Jun 2004 Lao wrote:

Some attributes are read only because it makes sense, not because the class author is mean.

A simple enough example is a Rectangle class with r/w attributes "width" and "height", but a r/o attribute "area". The area isn't necessarily even computed in the getter - it might be more efficient to compute it when the width or height properties are set, and cached.

I would generally allow another programmer to modify the area property, but make it clear that it is an internal property (single underscore prefix). If there's a really good reason to make width*height not equal to area, then go ahead... but be warned that you're doing something odd.

(#)
At 00:41, 04 Jun 2004 Alan Green wrote:

> A bit old-school static-typeist for my liking.

I know. I know. I argued with myself for a week about it too. In the end, I decided that read-only attributes were good documentation - they remind me that I "shouldn't" be changing those values. The cost is minimal: a line of code up-front (which replaces the "DON'T MODIFY THIS VALUE" comment), and the need to change the code later if it turns out that I was wrong and the attribute should be mutable.

(#)
At 10:18, 04 Jun 2004 Sean Ross wrote:

I've written some recipes related to automated property creation that you may find interesting:

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/157768
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252180

(#)
At 15:02, 04 Jun 2004 Gary Robinson wrote:

Coincidentally, I had the same idea about a week ago and have been using the same lambda-based idiom for properties.

(#)
At 16:31, 04 Jun 2004 Ian Bicking wrote:

Here's a module I use for generating these properties:

http://www.webwareforpython.org/~ianb/w4py.org/Wiki/lib/propertymeta.py

It automatically looks for specially-named methods (ending with __get, __set, or __del) and turns those into properties. Then all you need is to use the naming convention and to set the metaclass.

For read-only variables you can probably make it simpler still, with some of the techniques that have already been described -- though a custom descriptor class might be best, and easier to write than you might expect.

(#)
At 19:45, 04 Jul 2005 Ori Peleg wrote:

I think a property is a concept, not a data member. When you access x.template, why should you _care_ if it's a stored or computed value? That's the real power of the property() function. Bertrand Meyer's Uniform Access Principle and all that. Your data can start as a field and morph to a function whenever.

The downside is the inconvenient syntax. Contrast that with Ruby's oh-so-elegant

class Foo
  attr_accessor :bar
end

for a read/write field, and set/get can be defined at any time in the lifecycle of the code:

class Foo
  def bar
    # ...
  end
  def bar=(x)
    # ...
  end
end
(#)

Add Comment




(Not displayed)






(Leave blank line between paragraphs. URLs converted to links. HTML stripped. Indented source code will be formatted with <pre> tags.)




© 2003-2006 Alan Green