"Chris Angelico" wrote in message
news:captjjmps+vfu33tulae5oivrvn_otfuxrp8yluy68qmu36-...@mail.gmail.com...
On Thu, Jan 14, 2016 at 5:27 PM, Frank Millman <fr...@chagford.com> wrote:
> Using LBYL, one would retrieve the row(s) and check the length. I found
> a
> way to use EAFP, as follows -
>
> cur.execute('SELECT ...')
> (row,) = cur
>
> This uses tuple unpacking, and only works if exactly one row is
> returned. If
> it fails it raises ValueError, but I need to know whether it failed
> because
> no rows were returned, or more than one row was returned. The only way I
> could figure out how to achieve this was to parse the error message.
That seems like a tricky way to do things. How about this:
cur.execute(...)
rows = iter(cur)
try:
row = next(rows)
except StopIteration:
# 0 rows returned
try:
next(rows)
except StopIteration: pass
else:
# >1 rows returned
It's a bit more verbose, but it's reliable. Alternatively, since you
know that a returned row will never be None:
cur.execute(...)
rows = iter(cur)
row, extra = next(rows), next(rows)
if row is None:
# 0 rows returned
if extra is not None:
# >1 rows returned
Thanks, Chris. I did feel unhappy relying on the contents of an error
message, so I will follow up on your suggestion.
I like your first solution. In fact, with sqlite3, 'cur' is already
iterable, so I can just say
cur.execute(...)
try:
row = next(cur)
except StopIteration:
# 0 rows returned
try:
next(cur)
except StopIteration: pass
else:
# >1 rows returned
I will have to check if this works with psycopg2 and pyodbc, the other two
adaptors that I use.
Your second solution does not seem to work - if it raises StopIteration,
neither 'row' nor 'extra' is populated. I may be doing it wrong, but no
matter, I prefer the first one anyway.
There is one slight downside that I can see. I believe that raising an
exception is a relatively expensive operation in Python. For this reason, a
rule of thumb is to use EAFP when the usual case is to have no exception,
and LBYL otherwise. With my approach, I do not get an exception if exactly
one row is returned. With yours, you rely on raising an exception to
determine the result.
I can think of two counter-arguments to that. Firstly, it is more important
to ensure that the program is correct than to shave off a few microseconds.
Secondly, as StopIteration is used extensively under the hood in modern
pythons, it is probably highly optimised, and therefore nothing to be
worried about.
Frank
--
https://mail.python.org/mailman/listinfo/python-list