views:

100

answers:

3

I have an object that is basically a Python implementation of an Oracle sequence. For a variety of reasons, we have to get the nextval of an Oracle sequence, count up manually when determining primary keys, then update the sequence once the records have been inserted.

So here's the steps my object does:

  1. Construct an object, with a key_generator attribute initially set to None.
  2. Get the first value from the database, passing it to an itertools.count.
  3. Return keys from that generator using a property next_key.

I'm a little bit unsure about where to do step 2. I can think of three possibilities:

  1. Skip step 1 and do step 2 in the constructor. I find this evil because I tend to dislike doing this kind of initialization in a constructor.
  2. Make next_key get the starting key from the database the first time it is called. I find this evil because properties are typically assumed to be trivial.
  3. Make next_key into a get_next_key method. I dislike this because properties just seem more natural here.

Which is the lesser of 3 evils? I'm leaning towards #2, because only the first call to this property will result in a database query.

+2  A: 

I agree that attribute access and everything that looks like it (i.e. properties in the Python context) should be fairly trivial. If a property is going to perform a potentially costly operation, use a method to make this explicit. I recommend a name like "fetch_XYZ" or "retrieve_XYZ", since "get_XYZ" is used in some languages (e.g. Java) as a convention for simple attribute access, is quite generic, and does not sound "costly" either.

A good guideline is: If your property could throw an exception that is not due to a programming error, it should be a method. For example, throwing a (hypothetical) DatabaseConnectionError from a property is bad, while throwing an ObjectStateError would be okay.

Also, when I understood you correctly, you want to return the next key, whenever the next_key property is accessed. I recommend strongly against having side-effects (apart from caching, cheap lazy initialization, etc.) in your properties. Properties (and attributes for that matter) should be idempotent.

Sebastian Rittau
Excellent point. The fact that next_key wouldn't be idempotent is something I hadn't considered.
Jason Baker
+4  A: 

I think your doubts come from PEP-8:

    Note 3: Avoid using properties for computationally expensive
    operations; the attribute notation makes the caller believe
    that access is (relatively) cheap.

Adherence to a standard behavior is usually quite a good idea; and this would be a reason to scrap away solution #2.

However, if you feel the interface is better with property than a method, then I would simply document that the first call is more expensive, and go with that (solution #2).
In the end, recommendations are meant to be interpreted.

Roberto Liffredo
I suppose another issue would be "how exactly do you define computationally expensive"?At any rate, I've decided upon another route. I'll post it as a separate answer, but accept this since I feel this is the best answer in this regard.
Jason Baker
A: 

I've decided that the key smell in the solution I'm proposing is that the property I was creating contained the word "next" in it. Thus, instead of making a next_key property, I've decided to turn my DatabaseIntrospector class into a KeyCounter class and implemented the iterator protocol (ie making a plain old next method that returns the next key).

Jason Baker