• Ian's blog post on property

  last modified November 16, 2007 by novalis

A response to slinkP's post.

On properties in Python:

I think properties are just fine.  That you can't pass them around as callables doesn't seem very important.  Maybe it points to something wrong somewhere else.  Here's my general rules on properties:

1. They must never have side-effects (except caching, which shouldn't be noticeable from the outside).

2. They should be computationally cheap to get.  If something requires expensive calculation, it should be a method so the caller gets a hint at this.

3. They should not be very volatile.  If you get different values without something noticeable changing in the object, then a method call suggests that the return value is only applicable at *this moment*.  This would be a really bad property:

@property
def random_id(self):
    return random.randint(0, 1000)

It's okay if a property changes as a side-effect of something else, as long as the object was clearly changed somehow.

4. If there is a setter for the property, getting the property should give you back something like what you set the property to.  Some normalization of values on setting is okay.  This is bad:

def content__set(self, value):
    if value.startswith('http:'):
        value = urllib.urlopen(value).read()
    self._content = value
content = property(content__get, content__set)

5. Object identities should be fairly stable.  That is, "obj.foo is obj.foo" should be true.  For some kinds of Value Objects it's okay if this isn't true, so long as the objects are equivalent, equal, and immutable.  But it's better if it is true even in that case.

6. Just because you have a zero-argument attribute with no side-effects, you don't have to make it a property.

As for callbacks: why would you need a callback to a property?  It maybe be because rule 1 is broken, and you have to call the property at a specific point in time.  It might be rule 2 is broken, and you want to lazily get the value of the property to save work.  It might be rule 3, because the property must be accessed at a certain point in time to get the right value.  If none of these rules are broken and you need a callback (perhaps because the API you are using calls for a callback), then you just have to create a callback, probably like "lambda : obj.prop".  There's an itemgetter factory somewhere in the stdlib, but I can't remember where, and it's much more awkward than a lambda anyway.

As for Test Logic In Production, I don't agree with this article at all.  Mostly because I don't think the article actually makes any argument at all.  The alternative to such test logic is usually mock objects, or setups like dependency injection, both of which I consider far greater sins than test code in production.  All of these may be necessary at different times, and they are all smelly, but I don't think test logic in production is the smelliest of them.  The ideal solution is one where your APIs are strict enough and have a single point of entry, so that a test call and a real call are truly equivalent.  But if, say, you need to bypass authentication machinery to run a test with particular kinds of users as the principal, I think test logic in production is the best way.