When to use a model property vs. method?

8,255 views
Skip to first unread message

Micky Hulse

unread,
Jun 16, 2011, 2:07:15 PM6/16/11
to django...@googlegroups.com
Hello,

Just curious what the rule-of-thumb is for when it comes to using
model methods vs. properties. For example:

[code]

@property
def is_modified(self):
if self.modified > self.created:
return True
return False

def get_separator(self):
return ' :: '

[/code]

I feel like I have seen so many examples of methods and properties
that return the same type of stuff, I am just confused about when to
use one over the other.

Just hoping some of the pro Django/Python users on this list could
school me on this one. :D

Thanks so much!

Cheers,
Micky

Micky Hulse

unread,
Jun 16, 2011, 2:17:18 PM6/16/11
to django...@googlegroups.com
On Thu, Jun 16, 2011 at 11:07 AM, Micky Hulse <rgm...@gmail.com> wrote:
> Just hoping some of the pro Django/Python users on this list could
> school me on this one. :D

Actually, here's an example that I am working on now.

In my model, I have a CharField that holds the latitude/longitude with
values that look like:

44.08577787869324,-123.052459359169

I would like to write model methods to return latitude and longitude.

[code]

def lat(self):
# ...

def lng(self):
# ...

[code]

Should I use a property decorator?

Also, what is the difference between this:

[code]

def _get_lat(self):
# ...
lat = property(_get_lat)

[/code]

... and this:

[code]

@property
def lat(self):
# ...

[/code]

Sorry if stupid questions.

Thanks!
Micky

bruno desthuilliers

unread,
Jun 16, 2011, 4:42:48 PM6/16/11
to Django users


On 16 juin, 20:07, Micky Hulse <rgmi...@gmail.com> wrote:
> Hello,
>
> Just curious what the rule-of-thumb is for when it comes to using
> model methods vs. properties. For example:
>
> [code]
>
> @property
> def is_modified(self):
>     if self.modified > self.created:
>         return True
>     return False

<ot>
This could be written much more simply as "return self.modified >
self.created"
</ot>


> def get_separator(self):
>     return ' :: '

In this case (returning a constant), it would be much simpler to make
it a class (or instance) attribute.

> [/code]
>
> I feel like I have seen so many examples of methods and properties
> that return the same type of stuff, I am just confused about when to
> use one over the other.

Properties are for computed attributes - that is, things that are the
semantically (conceptually, whatever) attributes, but requires any
computation, either on get, set or del. But well, if you prefer java-
like getters/setters, then choice is yours... Depends on the
programmer's background, and of course of whether the code was written
before new-style classes and the descriptor protocol....


Micky Hulse

unread,
Jun 16, 2011, 5:35:42 PM6/16/11
to django...@googlegroups.com
Hi Bruno! Thank you so much for your help. I really appreciate the pro
assistance. :)

On Thu, Jun 16, 2011 at 1:42 PM, bruno desthuilliers
<bruno.des...@gmail.com> wrote:
> <ot>
> This could be written much more simply as "return self.modified >
> self.created"
> </ot>

Omg, thanks! That's way better. :D

>> def get_separator(self):
>>     return ' :: '
> In this case (returning a constant), it would be much simpler to make
> it a class (or instance) attribute.

Ah, I think I get it.

Just to clarify things for me, does attribute equal a property or method? :D

I am guessing that attribute = property, so you're saying that I
could/should do this:

@property
def separator(self):
return ' :: '

> Properties are for computed attributes - that is, things that are the
> semantically (conceptually, whatever) attributes, but requires any
> computation, either on get, set or del.

Ah, interesting... I think I need to let this all sink in a little. :)

> But well, if you prefer java-
> like getters/setters, then choice is yours... Depends on the
> programmer's background,

I have a little experience with Actionscipt 3 and PHP5 OOP... I guess
I am used to the concept/practice of getters/setters,
(public/private/other) class methods and properties.

Are you saying that a non-property model class method is equivalent to
getter/setter? Or, maybe you are saying that the "lat =
property(_get_lat)" python property syntax is like
getters/setters/accessors in other programming langs?

Again, sorry if stupid questions... sorry if this is getting OT for
the Django list.

> and of course of whether the code was written
> before new-style classes and the descriptor protocol....

I have not heard of this before. Googling "new-style classes and the
descriptor protocol" now. ;)

Looks like I have a lot of learning to do! :D

Thanks again, I greatly appreciate your time and assistance.

Have a great day.

Cheers,
Micky

Micky Hulse

unread,
Jun 16, 2011, 5:51:11 PM6/16/11
to django...@googlegroups.com
How's this for a rule-of-thumb(?):

1. Use a property (class/instance attribute) when an argument(s) is not needed.

2. If an argument(s) is needed (for runtime(?) calculations and/or
class/instance attributes need to be sanitized/checked/other), then
use a method (or getter/setter/descriptor).

No?

Hehe, thanks again!!!

Cheers,
Micky

Micky Hulse

unread,
Jun 16, 2011, 6:00:33 PM6/16/11
to django...@googlegroups.com
I am reading through Chapter5+ of Dive Into Python now... Seems like
that should cover all of my questions! :D

Sorry to bug the list with my ramblings.

Thanks again Bruno!

Have a great day all!
Cheers,
Micky

Tom Evans

unread,
Jun 17, 2011, 4:42:19 AM6/17/11
to django...@googlegroups.com
On Thu, Jun 16, 2011 at 10:35 PM, Micky Hulse <rgm...@gmail.com> wrote:
> Just to clarify things for me, does attribute equal a property or method? :D
>
> I am guessing that attribute = property, so you're saying that I
> could/should do this:
>
> @property
> def separator(self):
>    return ' :: '
>

No, he's saying that is ridiculous overkill for a constant attribute.

class MyModel(models.Model):
SEPARATOR = '::'

Access it with MyModel.SEPARATOR or my_model_instance.SEPARATOR

I use property() when either the setting or modification of an
attribute requires me to run actual code in order to manipulate the
supplied value into a different format. Eg, if I am storing a comma
separated list of IP addresses as strings in a database field, I don't
want to have to deal with splitting, parsing and turning it into a
list of proper objects in every different place. Instead, I supply a
property that serializes/deserializes the data as needed.

Another reason to use property() is when you remove an attribute that
is part of your public API, you can provide a property with the same
name as the attribute. Eg:

class MyModel(models.Model):
@property
def SEPARATOR(self):
if self.foo:
return '::'
return '||'

The behaviour is now dynamic, but any old code using
instance.SEPARATOR will get still work, and use the new dynamic
definition.

Cheers

Tom

bruno desthuilliers

unread,
Jun 17, 2011, 5:41:54 AM6/17/11
to Django users
On Jun 16, 11:51 pm, Micky Hulse <rgmi...@gmail.com> wrote:
> How's this for a rule-of-thumb(?):
>
> 1. Use a property (class/instance attribute) when an argument(s) is not needed.

Well, a setter does need an argument ;)

bruno desthuilliers

unread,
Jun 17, 2011, 6:18:56 AM6/17/11
to Django users
On Jun 16, 11:35 pm, Micky Hulse <rgmi...@gmail.com> wrote:
>
> >> def get_separator(self):
> >>     return ' :: '
> > In this case (returning a constant), it would be much simpler to make
> > it a class (or instance) attribute.
>
> Ah, I think I get it.
>
> Just to clarify things for me, does attribute equal a property or method? :D

Python's object model is a bit peculiar (wrt/ most OOPLs at least),
and I won't start to explain it here (this would require a full book
FWIW). To make a long story short, you have class and instance
attributes, and since a small code snippet can say more than a
thousand words:

class Foo(object):
bar = 42 # this a class attribute

def __init__(self, yoot):
self.yoot = yoot # this is an instance attribute

# this is an instance method
def baaz(self):
print "%s.baaz" % self

# this is a class method
@classmethod
def yuu(cls):
print "%s.yuu" % cls

> I am guessing that attribute = property

Nope. "property" is a builtin Python class that implements the
descriptor protocol and give support for simple computed attributes.



> @property
> def separator(self):
>     return ' :: '

This is even more overkill. In Python, if all you need is a plain
attribute, just use a plain attribute - you can transparently turn it
into a computed one later if needed so no need to waste time with
getters/setters.




> > Properties are for computed attributes - that is, things that are the
> > semantically (conceptually, whatever) attributes, but requires any
> > computation, either on get, set or del.
>
> Ah, interesting... I think I need to let this all sink in a little. :)
>
> > But well, if you prefer java-
> > like getters/setters, then choice is yours... Depends on the
> > programmer's background,
>
> I have a little experience with Actionscipt 3 and PHP5 OOP... I guess
> I am used to the concept/practice of getters/setters,
> (public/private/other) class methods and properties.

Warning: in Python, a "class method" is a method that can be called
either on the class or instance, AND takes the class as first
argument. "ordinary" methods - the ones that takes the instance as
first argument - are also called "instance methods".

wrt/ "public/private", there's no such thing in Python anyway. The
(*very* strong) convention is to prefix implementation stuff with a
single leading underscore which means "don't touch or your on your own
if something breaks", but nothing prevents client code to mess with
some object's internals.


> Are you saying that a non-property model class method is equivalent to
> getter/setter?

No.

> Or, maybe you are saying that the "lat =
> property(_get_lat)" python property syntax is like
> getters/setters/accessors in other programming langs?

Mostly, yes - with the exception that since Python can let you turn a
plain attribute into a computed one without breaking client code, you
don't have to use getters / setters "just in case" - you only use them
if and when needed, and encapsulate them into a property (or a custom
descriptor) so the client code don't have to care.

> Again, sorry if stupid questions... sorry if this is getting OT for
> the Django list.

Well, I usually redirect this kind of questions to c.l.py, since it's
about Python and not Django...

> > and of course of whether the code was written
> > before new-style classes and the descriptor protocol....
>
> I have not heard of this before. Googling "new-style classes and the
> descriptor protocol" now. ;)

Warning: this (and metaclasses) can be a bit mind-bending at first,
and tend to become highly addictive once you get it ;)

If I may suggest another related reading:
http://wiki.python.org/moin/FromFunctionToMethod


FWIW, Django relies quite a lot on metaclasses and descriptors for the
ORM part.

Micky Hulse

unread,
Jun 17, 2011, 3:24:35 PM6/17/11
to django...@googlegroups.com
Hi Tom and Bruno! Thank you so much for your pro help and assistance.
I really appreciate it. :)

Replying to both of you in this one e-mail... See inline replies below.

On Fri, Jun 17, 2011 at 1:42 AM, Tom Evans <teva...@googlemail.com> wrote:
> Access it with MyModel.SEPARATOR or my_model_instance.SEPARATOR

Ahhh, I see now. Thanks for the clarification!

I now plan on re-factoring my simple "category/sub-category" model:

https://gist.github.com/1031882

> ...<snip>...


> list of proper objects in every different place. Instead, I supply a
> property that serializes/deserializes the data as needed.

Awesome. That's a good description.

I have a few spots in my latest app that needs this type of
functionality, I will be sure to use property().

Not to state the obvious, but I just learned/realized that the
property decorator is Python 2.4's version of foo =
property(get_foo)... For some reason, up until now, I was under the
assumption that the property decorator was a Django thing! :D

> ...<snip>...


> The behaviour is now dynamic, but any old code using
> instance.SEPARATOR will get still work, and use the new dynamic
> definition.

Ooh, that is interesting too! Thank you for the explanations and code
examples, that really helps clear things up for me. :)

On Fri, Jun 17, 2011 at 3:18 AM, bruno desthuilliers
<bruno.des...@gmail.com> wrote:
> Python's object model is a bit peculiar (wrt/ most OOPLs at least),
> and I won't start to explain it here (this would require a full book
> FWIW).

Hehe!

Yah, you know, I am one of those few folks that learned (er, is
learning) Django before getting to know Python...

:: hangs head in shame ::

I am planning on spending a few upcoming weekends reading through the
online version of Dive into Python. :)

> To make a long story short, you have class and instance
> attributes, and since a small code snippet can say more than a
> thousand words:

> ...<snip>...


> # this is a class method
> @classmethod
> def yuu(cls):
> print "%s.yuu" % cls

Oh, perfect! That really helps clarify things! Thanks for the code example. :)

I have never used the @classmethod decorator before. I will have to
read up on that one.

Looks like there's also @staticmethod... Interesting stuff! Thanks!

>> I am guessing that attribute = property
> Nope. "property" is a builtin Python class that implements the
> descriptor protocol and give support for simple computed attributes.

Ahh, I see now! Thanks for the clarification.

> ...<snip>...


> you can transparently turn it
> into a computed one later if needed so no need to waste time with
> getters/setters.

Awesome! I get it now. :)

> ...<snip>...


> wrt/ "public/private", there's no such thing in Python anyway. The
> (*very* strong) convention is to prefix implementation stuff with a
> single leading underscore which means "don't touch or your on your own
> if something breaks", but nothing prevents client code to mess with
> some object's internals.

I am reminded of a sidebar that I read in the Dive Into Python book:

"There are no constants in Python. Everything can be changed if you
try hard enough. This fits with one of the core principles of Python:
bad behavior should be discouraged but not banned. If you really want
to change the value of None, you can do it, but don't come running to
me when your code is impossible to debug."
-- <http://diveintopython.org/object_oriented_framework/class_attributes.html>

... specifically, that last two sentences! :)

> Mostly, yes - with the exception that since Python can let you turn a
> plain attribute into a computed one without breaking client code, you
> don't have to use getters / setters "just in case" - you only use them
> if and when needed, and encapsulate them into a property (or a custom
> descriptor) so the client code don't have to care.

That's a great explanation. Thanks!

> Well, I usually redirect this kind of questions to c.l.py, since it's
> about Python and not Django...

I have not hear of c.l.py... I assume that it is a Python listserv?
Googling now. :)

I am a member of the Google Python group, but I am not a huge fan of
digest e-mails... I would love to find a python list that allowed for
e-mail for every message.

> Warning: this (and metaclasses) can be a bit mind-bending at first,
> and tend to become highly addictive once you get it ;)
> If I may suggest another related reading:
> http://wiki.python.org/moin/FromFunctionToMethod

Ooooh, interesting! Thanks for the linkage! Much appreciated.

Looks like my weekend is going to be full of pure python coding!

Also, thanks for pointing towards the wiki:

http://wiki.python.org/

That site looks useful.

> FWIW, Django relies quite a lot on metaclasses and descriptors for the
> ORM part.

Good to know. All of this information will give me stuff to research
over then next several weeks.

Thanks again Tom and Bruno! You folks ROCK! :D

I owe you one.

Have a nice day.

Cheers,
Micky

bruno desthuilliers

unread,
Jun 18, 2011, 6:14:29 AM6/18/11
to Django users


On 17 juin, 21:24, Micky Hulse <rgmi...@gmail.com> wrote:
>
> I have never used the @classmethod decorator before. I will have to
> read up on that one.

A classmethod is a method that takes the class itself as first
argument, and so can be called either on a class or instance. It's
very handy for alternate constructors or such and some interesting
patterns...

> Looks like there's also @staticmethod... Interesting stuff! Thanks!

Well, Python is 100% OO in that everything you can bind to a name is
an object (classes, functions, modules etc), but it's not "pure" OO in
that it doesn't force you into using classes when you just don't need
them. This makes the staticmethod much less useful, since most of the
time a plain function will be enough.

>
> I am reminded of a sidebar that I read in the Dive Into Python book:
>
> "There are no constants in Python."

Nope but there's another very strong convention here: ALL_UPPER_NAMES
are to be considered as constants.

> " Everything can be changed if you
> try hard enough. This fits with one of the core principles of Python:
> bad behavior should be discouraged but not banned. If you really want
> to change the value of None, you can do it,"

That's not true anymore FWIW:

Python 2.6.6 (r266:84292, Sep 15 2010, 15:52:39)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> None = 42
File "<stdin>", line 1
SyntaxError: assignment to None
>>>

but you can still do some other very weird things like:

>>> False = True
>>> False
True


> " but don't come running to
> me when your code is impossible to debug."

Indeed.

>
> > Well, I usually redirect this kind of questions to c.l.py, since it's
> > about Python and not Django...
>
> I have not hear of c.l.py... I assume that it is a Python listserv?

Usenet newsgroup comp.lang.python. You can also access it via
googlegroups too.

> Also, thanks for pointing towards the wiki:
>
> http://wiki.python.org/
>
> That site looks useful.

http://python.org/doc/ should be your starting point.

HTH

Ethan Jucovy

unread,
Jun 18, 2011, 9:05:21 AM6/18/11
to django...@googlegroups.com
On Thu, Jun 16, 2011 at 2:07 PM, Micky Hulse <rgm...@gmail.com> wrote:
Hello,

Just curious what the rule-of-thumb is for when it comes to using
model methods vs. properties. For example:

[code]

@property
def is_modified(self):
   if self.modified > self.created:
       return True
   return False

def get_separator(self):
   return ' :: '

[/code]

I feel like I have seen so many examples of methods and properties
that return the same type of stuff, I am just confused about when to
use one over the other.

I tend to avoid @property -- it adds conceptual overhead for code readers (hiding the fact that a function call is taking place) for little gain.

The main use for @property is backwards-compatibility: if my code used to have an attribute, and I now need to compute something instead of returning an attribute directly, I'll use @property so that calling code doesn't need to change.

Beyond that, my preferred rule of thumb is that I will not use @property if any non-trivial computation takes place.  But I try to avoid it even for trivial computations.  Methods are more self-documenting and more flexible (e.g. less refactoring needed if you end up adding function arguments later)

The one exception is adaptation.  If I have a method that returns another object that's an API wrapper which I'll be accessing immediately, I'll use @property -- e.g.

class MyModel(models.Model):
  @property
  def printer(self):
    return Printer(self)

class Printer(object):
  def __init__(self, inst):
    self.inst = inst
  def get_value(self):
    return "something"
  def print(self, out):
    out.write( str(self.inst) + self.get_value() )
 
In a case like this I use property so that I can write `myobject.printer.print(foo)` instead of `myobject.printer().print(foo)` which just looks too ugly for me to ignore.

For your lat/lng example I'd probably just write a single model method (not a property) that parses the CharField and returns a 2-tuple (lat, long) and another which sets them given two arguments.  (How often do you use one without the other, anyway; the fact that they're conceptually coupled seems more likely to survive refactoring than the storage mechanism and format.)

-Ethan

Masklinn

unread,
Jun 18, 2011, 10:01:57 AM6/18/11
to django...@googlegroups.com
On 2011-06-18, at 15:05 , Ethan Jucovy wrote:
> I tend to avoid @property -- it adds conceptual overhead for code
> readers (hiding the fact that a function call is taking place) for little
> gain.
You *do* realize that django model (and form) fields are descriptors, and any access to a model or form field will result in a bunch of method calls right?

bruno desthuilliers

unread,
Jun 18, 2011, 10:07:16 AM6/18/11
to Django users
On 18 juin, 15:05, Ethan Jucovy <ethan.juc...@gmail.com> wrote:
> I tend to avoid @property -- it adds conceptual overhead for code
> readers (hiding the fact that a function call is taking place) for little
> gain.

Would you say that the way the attribute lookup mechanism and
descriptor protocol are used to turn function class attributes into
methods "adds conceptual overhead for little gain" and that it would
be better to have to explicitely lookup the function on the class and
explicitely pass the instance, ie write:

type(obj).method(obj, arg)

instead of

obj.method(arg)

?

> Beyond that, my preferred rule of thumb is that I will not use @property if
> any non-trivial computation takes place.

That's a POV. I personnaly tend to use properties (or custom
descriptors) for anything that has the semantics of an attribute and
is either trivial to compute or trivial to memoize.

>  But I try to avoid it even for
> trivial computations.  Methods are more self-documenting and more flexible
> (e.g. less refactoring needed if you end up adding function arguments later)

If you ever have to "end up adding arguments later", then it wasn't
really an attribute and should have been a method right from the
start. Design mistake - and yes, it happens, I don't mean I'm smarter
than you ;)

But even then, since adding extra params means you'll have to fix
client code, the "less refactoring" argument doesn't stand IMHO.


All this being said, I wholefully agree that while computed attributes
are very handy, one should not go over the top and use them for no
good reason - simple code is hard enough to maintain.

Ethan Jucovy

unread,
Jun 18, 2011, 11:20:31 AM6/18/11
to django...@googlegroups.com
On Sat, Jun 18, 2011 at 10:01 AM, Masklinn <mask...@masklinn.net> wrote:
On 2011-06-18, at 15:05 , Ethan Jucovy wrote:
> I tend to avoid @property -- it adds conceptual overhead for code
> readers (hiding the fact that a function call is taking place) for little
> gain.
You *do* realize that django model (and form) fields are descriptors, and any access to a model or form field will result in a bunch of method calls right?
On Sat, Jun 18, 2011 at 10:07 AM, bruno desthuilliers <bruno.des...@gmail.com> wrote:
Would you say that the way the attribute lookup mechanism and
descriptor protocol are used to turn function class attributes into
methods "adds conceptual overhead for little gain"

These caricatures are a little unnecessary, aren't they?  Obviously this is just the way that I like to lean in my own code (which was what the OP asked) not a hard and fast rule that I want to apply to all Python code regardless of the situation.

But even then, since adding extra params means you'll have to fix
client code, the "less refactoring" argument doesn't stand IMHO.

My downstream code styles differ for the two cases -- for example, when I'm accessing attributes I access them as needed, even if I re-get the same attribute multiple times in a single code block; when I'm using the return value of a function I store it in a local variable rather than doing multiple calls.  So I still have to fix client code, but *less* of it.  Etc.

All this being said, I wholefully agree that while computed attributes
are very handy, one should not go over the top and use them for no
good reason - simple code is hard enough to maintain.

Exactly, that's really all I meant.  When I started out with Python I totally overused @property including for things that were semantically zero-argument methods (I'm really not sure why) and my code became unnecessarily hard to maintain and reason about.  So now I like to lean in the other direction.  I still use properties plenty, but I like to keep some pushback in my head.  Generally I'd rather refactor a method into a property than a property into a method.

Micky Hulse

unread,
Jun 20, 2011, 1:36:53 PM6/20/11
to django...@googlegroups.com
Thanks to everyone (Bruno, Tom, Ethan and Masklinn) for the extremely
informative and very helpful replies.

I really appreciate all of the professional help.

I have a lot to research and need to practice some of these newly
learned techniques/concepts... I may be back with more (this time,
Django-centric) questions. :D

Thanks! Have a great week!

Cheers,
Micky

Reply all
Reply to author
Forward
0 new messages