To genexp or not to genexp - a cautionary tale
I don’t know about you but I’ve been increasingly comfortable using a genexp style when passing sequences as parameters. It just saves a bit of syntax clutter and generally does exactly what you want. To give a completely trivial example:
a = range (10) print sorted (i for i in a if i < 7)
But (there’s always a But) I found myself having unexpected problems with a database-row class I’d recently beefed up and I couldn’t work out why. It’s one of these things I’m sure everyone’s done where you pass it the cursor description and the row values and it does the __getattr__ and __getitem__ stuff for you. Well, it was acting as though the cursor wasn’t passing the column names at all. I suspected the new(ish) version of pyodbc I’d installed lately, but it was soon clear that the problem was in the generated Row class itself.
Of course, it turned out that I was, as above, passing the column names as a genexp. And some early reference was consuming the generator, silently leaving nothing for a later reference to consume. Something like this:
q.execute ("SELECT blah FROM blah") Row = row.Row (d[0] for d in q.description) return [Row (i) for i in q.fetchall ()]
where the code in row.Row did something like this:
def Row (names): something = "-".join (names) ## Oops, consumed the generator description = dict ((name, index) for index, name in enumerate (names)) ## Oh dear, nothing left to consume but not an error
You see that I’m not falling into the trap of returning the list of rows as a genexp, because I know I’m likely to be using it in several places, but it’s just that bit cleaner to pass as a genexp as a parameter, and I fell for it.
One more thing to look out for.