Why don't generators execute until first yield?

Martin Sand Christensen msc at es.aau.dk
Wed May 7 04:29:57 EDT 2008


Hi!

First a bit of context.

Yesterday I spent a lot of time debugging the following method in a
rather slim database abstraction layer we've developed:

,----
| def selectColumn(self, table, column, where={}, order_by=[], group_by=[]):
|     """Performs a SQL select query returning a single column
|
|     The column is returned as a list. An exception is thrown if the
|     result is not a single column."""
|     query = build_select(table, [column], where, order_by, group_by)
|     result = DBResult(self.rawQuery(query))
|     if result.colcount != 1:
|         raise QueryError("Query must return exactly one column", query)
|     for row in result.fetchAllRowsAsList():
|         yield row[0]
`----

I'd just rewritten the method as a generator rather than returning a
list of results. The following test then failed:

,----
| def testSelectColumnMultipleColumns(self):
|     res = self.fdb.selectColumn('db3ut1', ['c1', 'c2'],
|                                 {'c1':(1, 2)}, order_by='c1')
|     self.assertRaises(db3.QueryError, self.fdb.selectColumn,
|                       'db3ut1', ['c1', 'c2'], {'c1':(1, 2)}, order_by='c1')
`----

I expected this to raise a QueryError due to the result.colcount != 1
constraint being violated (as was the case before), but that isn't the
case. The constraint it not violated until I get the first result from
the generator.

Now to the main point. When a generator function is run, it immediately
returns a generator, and it does not run any code inside the generator.
Not until generator.next() is called is any code inside the generator
executed, giving it traditional lazy evaluation semantics. Why don't
generators follow the usual eager evaluation semantics of Python and
immediately execute up until right before the first yield instead?
Giving generators special case semantics for no good reason is a really
bad idea, so I'm very curious if there is a good reason for it being
this way. With the current semantics it means that errors can pop up at
unexpected times rather than the code failing fast.

Martin



More information about the Python-list mailing list