Empty anvil.tables.SearchIterator should be "False"

I some code like this:

rows = app_tables.qsessions.search(...)
if rows:
    <do something if more than 0 rows exist>  

However this fails because an empty iterator evaluates to True. To me this seems counterintuitive as an empty list (or similar) evaluates to False.

Of course this can be fixed by converting to a list but I might not need the rows. And it increases the cognitive burden.

2 Likes

You can efficiently get the length of a search iterator with len():

if len(rows):
  # ... do something...

Yes, thanks. But wouldn’t you agree that the truthiness of an empty iterator should be False?

1 Like

How does that compare to your experience with other iterators in Python?

Here’s what I get with a regular iterator:

>>> i = iter([1,2,3])
>>> bool(i)
True
>>> i = iter([])
>>> bool(i)
True

This is one thing that makes an iterator different from a container. An empty container is “falsy”. Virtually all other “objects” are “truthy”.

1 Like

Hmmm, hadn’t realised that. Thanks.
Is this the case for every iterator (by specification)?

As I understand it, by default, every Python object is “truthy”, except for a few, select cases:

  • None
  • False
  • Zero
  • Empty containers (including strings, tuples, sets, lists, dicts, …)
  • Objects of custom-built classes, if they were explicitly built to override that default

I’m not familiar with any such custom classes. I suspect they’re fairly rare.

Iterators are not among the exceptions, so they’re “truthy”.

Reference: Truth Value Testing

Edit: To be fair, it isn’t necessarily a bad idea. C++ has the concept of “empty ranges”, which test false when the range is exhausted. This method of stopping is handy in situations where Python’s exception-based approach (raise StopIteration when done) is too slow or bulky.

But there are situations where determining “emptiness” can be expensive. For example, an iterator backed by a network socket, and a buffer. If the buffer is empty, then the iterator might be “empty”; but that could change in a microsecond, as data arrives from across the network. In this case, Python’s iterator model, which is more general, looks to me like a better fit.

By now, there’s on the order of a billion lines of Python code that work under the above rules. Changing the rules now is probably not an option.

1 Like

Thx for the background info.

Came back to this, as I’ve hit this too. Going off the above linked Truth Value testing, the following

if rows:
  # ... do something...

definitely is more Pythonic, compared to the suggested:

if len(rows):
  # ... do something...

version. I haven’t really seen the latter used, though some go with the more explicit

if len(rows) > 0:
  # ... do something...

version, which works here too, of course, just unnecessarily verbose.

Though maybe this departure from the Python list behaviour is somewhat expected, since the SearchIterator doesn’t seem to behave like an iterator either (more like a list in every other aspect), so I’m sure there are preconceptions and expectations that get broken :slight_smile:

Still, if it “walks like list and quacks like list”, maybe it should be truthy like a list? :thinking: But I can totally be missing something too.