Let's say you're hashing out a Python model class (Django, TurboGears, anything -- doesn't matter). Following the Skinny Controller, Fat Model approach, you want to add some logic and convenience methods to the model, rather than stuffing them into the one or more controllers.
Here's a basic example using the SQLAlchemy ORM:
class Product(Base): __tablename__ = TABLE id = Column(Integer, primary_key=True) title = Column(TITLE_FIELD, String) description = Column(DESC_FIELD, String) def get_link(self, link=ITEM_LINK): return link % self.id
The get_link() method calls a predefined string, and uses string formatting to insert the class variable id. You might think you could directly define this with a variable, rather than a method. Go ahead, give it a try. But because it calls another (not yet defined) class variable, that simply won't work. That's just a fundamental reality of object-oriented code, at least in the Python world.
So we end up with an object that works as follows, assuming item is an instance of Product:
item.title item.description item.get_link()
That last line just doesn't feel right, does it? It ought to be accessible through an attribute, rather than a method -- as with the title and description. After all, we are talking about a noun here, not a verb. This works, but it's inconsistent and inelegant.
Thanks to some perusal of Django models, I found the perfect solution in Python's built-in property() method.
We can modify the class slightly as follows:
class Product(Base): __tablename__ = TABLE id = Column(Integer, primary_key=True) title = Column(TITLE_FIELD, String) description = Column(DESC_FIELD, String) def _get_link(self, link=ITEM_LINK): return link % self.id link = property(_get_link)
And with that simple modification, our item instance works as follows:
item.title item.description item.link
There, doesn't that feel better?
For much more on property(), including how to use for getter and setter methods, and lots of other useful info, see this excellent post courtesy of Adam Gomaa.